Safely managing threads from another thread (WinAPI)

  • (2 Pages)
  • +
  • 1
  • 2

19 Replies - 1536 Views - Last Post: 07 September 2012 - 10:53 AM Rate Topic: -----

#1 nullcoding  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 99
  • Joined: 08-July 12

Safely managing threads from another thread (WinAPI)

Posted 03 September 2012 - 11:31 AM

For those of you who have seen me before, I am indeed asking about the same application on which I was working previously.

I have implemented an experimental number-testing method in my program that I am currently testing out for stability. It turns out that it is not very stable at all.

From the "main" thread, responsible for drawing the GUI, you press a button. If certain check boxes are checked, it calls six different functions.

Each of these functions takes three parameters, one of which is a newly-initialized instance of a structure to which the other two parameters are passed. This function calls other functions via pointers, as these other functions are part of a structure in order to run in a separate thread.

Simply put, you call six functions and each starts a new thread.

My question has a few parts. I find myself in a strangely paradoxical situation, whereby each thread is initialized with a handle to itself that I can then pass to the SuspendThread() or TerminateThread() functions. The problem is that in order for this to make any sense, the handle is a member of the structure accessed by the thread. Thus I do it like this:

	pprothstructvr1->Thread1=CreateThread(0,
		0,
		TestProthVR1,
		pprothstructvr1,
		0,
		pprothstructvr1->ProthThread1);



where "pprothstructvr1" is a parameter passed to the TestProthVR1 function - indeed this parameter is the instance of the structure initialized on the main thread.

Thread1, in this case, is a LPDWORD handle initialized such that I can then call other things using it. For instance, I have a button on the main window that, when pressed, is meant to pause or resume the number testing (that is, suspend/resume all 6 threads) like this:

void PauseProthVR1(ProthStructVR1 * pprothstructvr1)
{
	SuspendThread(pprothstructvr1->Thread1);
}

void ResumeProthVR1(ProthStructVR1 * pprothstructvr1)
{
	ResumeThread(pprothstructvr1->Thread1);
}



This works well enough. However, suppose I wish to cancel an in-progress test (that is, end the thread before the big intense function actually returns). I have a button called STOP that currently calls TerminateThread(handle, return code).

Being unfamiliar with multi-threading, I was unaware that TerminateThread() ought only be used, according to MSDN, in "extreme circumstances." I stupidly put TerminateThread() at the end of the functions meant to end the thread (of which there are 3).

In my experience, if I call ExitThread() or something like that, it ends the calling thread, so if I were to call that function from the main window, it would close the application and kill the main process - not helpful!

I read this at MSDN:

Quote

...in C++ code, the thread is exited before any destructors can be called or any other automatic cleanup can be performed. Therefore, in C++ code, you should return from your thread function.


Now, I think I understand that. Somewhat. However, my threads are structured like this:

1. main window calls "start" function using structure name
2. "start" function passes pointers to the structure's members
3. "start" function calls CreateThread() with the "Testing" function as its starting address
4. "Testing" function does operations on/using members of the structure in a "for" loop
5. If condition X is satisfied, "Testing" function calls the "Composite" function and breaks the "for" loop
6. If condition Y is satisfied, "Testing" function calls the "Prime" function and breaks the "for" loop
7. The "Prime" and "Composite" function both do the same thing - display results and end the thread.

I'm sorry if this is complex, but thank you for bearing with me so far.

Here is the paradox I mentioned earlier (at least it seems like one to me):

In order to clean up memory, I need to delete the structure I initialized. However, if I do that before I end the thread, I am unable to pass the thread handle to the exit function. If I do that after I end the thread, it will never get deleted because the thread using the structure has already ended!

I do know about the WaitForSingleObject() function but have never used it. I am not 100% sure about thread management.

When I run my program in a Windows environment, it will carry out one test without a hitch. If I try to perform another, however, it seizes up! Running it through the Visual Studio debugger, moreover, throws AccessViolations and references KernelBase.dll without performing a single test.

My initial thinking was that setting stack size to 0 was a bad idea, as what's defined in the application header as the default stack size is almost definitely not enough. It's just that the last time I played with memory management, my little application was eating 1,7GB of RAM and accomplishing very little.

I also know that TerminateThread() is NOT what I should be calling. But then how do I cancel the test (that is, stop the thread before it returns)?

If I've opened up a can of worms with VC++ and/or Windows itself, do let me know. If there's a better way of multi-threading, I'd love to know what it is.

Thanks!

Is This A Good Question/Topic? 0
  • +

Replies To: Safely managing threads from another thread (WinAPI)

#2 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 03 September 2012 - 02:12 PM

Are you missing an ampersand on line 6:
pprothstructvr1->Thread1=CreateThread(0,
	0,
	TestProthVR1,
	pprothstructvr1,
	0,
	pprothstructvr1->ProthThread1);



Or is ProthThread1 truly a LPDWORD that has been correctly initialized to a point to DWORD that will hold the thread ID?
Was This Post Helpful? 0
  • +
  • -

#3 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 03 September 2012 - 02:22 PM

As for cleaning up memory... what is wrong with this model that assumes that your main UI thread never needs access to the thread data:
struct ThreadData
{
    DWORD threadID;
    :
};

void OnCommand_Start()
{
    ThreadData * pdata = new ThreadData;
    // initialize pdata
    CreateThread(.., DoWork, pdata, ...);
}

DWORD DoWork(void *pv)
{
    ThreadData * pdata = reinterpret_cast<ThreadData *>(pv);
    pdata->threadID = GetCurrentThreadId();

    // Do all the work with pdata

    delete pdata;
    return 0;
}


Was This Post Helpful? 0
  • +
  • -

#4 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 03 September 2012 - 03:31 PM

And to support pause, resume, and stop functionality, something like this can be done:
struct ThreadInfo
{
    HANDLE hThread;
    HANDLE hStopEvent;
};

struct ThreadData
{
    DWORD threadID;
    HANDLE hStopEvent;
    :
};

class MainWindow
{
    ThreadInfo m_threadinfo;

    void OnCommand_Start()
    {
        m_threadinfo.hStopEvent = CreateEvent(...);

        ThreadData * pdata = new ThreadData;
        pdata->hStopEvent = m_threadinfo.hStopEvent;

        m_threadinfo.hThread = CreateThread(.., DoWork, pdata, ...);
        :
    }

    void OnCommand_Pause()
    {
        SuspendThread(m_threadinfo.hThread);
    }

    void OnCommand_Resume()
    {
        ResumeThread(m_threadinfo.hThread);
    }

    void OnCommand_Stop()
    {
        SetEvent(m_threadinfo.hStopEvent);
    }

};

DWORD DoWork(void *pv)
{
    ThreadData * pdata = reinterpret_cast<ThreadData *>(pv);
    pdata->threadID = GetCurrentThreadId();

    // Do all the work with pdata
    for(...)
    {
        // Do a quick check to see if the stop button has been pressed
        DWORD dw = WaitForSingleObject(pdata->hStopEvent, 0);
        if (dw == WAIT_OBJECT_0)
            break;

        :
    }

    delete pdata;
    return 0;
}


Was This Post Helpful? 1
  • +
  • -

#5 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 03 September 2012 - 03:40 PM

As for your "Composite" and "Prime" functions, why do they even have to stop the thread? The thread will stop on it's own:
DWORD DoWork(void *pv)
{
    ThreadData * pdata = reinterpret_cast<ThreadData *>(pv);

    // Do all the work with pdata
    for(...)
    {
        :

        if (ConditionX())
        {
            Composite();
            break;
        }

        if (ConditionY())
        {
            Prime();
            break;
        }

        :
    }

    delete pdata;
    return 0;
}


Was This Post Helpful? 0
  • +
  • -

#6 nullcoding  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 99
  • Joined: 08-July 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 06:36 AM

Alright, so I'm not at my coding machine right now, but I can tell you that yes there is no ampersand there and no the LPDWORD is not initialized in the sense you mean.

Hmm so the main function (which you call DoWork) should not end the thread because it calls one of two functions depending on the results it gets. It does return 0, since it is a DWORD WINAPI function but it ought not end the thread or anything.

You're saying that if I simply have something like this:


void CalcProthPrime(pprothstructvr1)
{

// do stuff to say it's a prime number
..
..
delete pprothstructvr1

}




the thread will return/end on its own? I got the impression that beginning a thread with the CreateThread() function necessitated the use of ExitThread() as well...(as opposed to normal CRT threading etc etc)

I throw the pprothstructvr1 pointer around a lot...it gets passed to a lot of different functions, mostly for the sake of code cleanup and organization. I think it was you who initially told me to have helper functions instead of a single massive function rife with confusing conditionals! (glad I listened)

So essentially, the Test() function ought to return 0, but not end the thread, which leads me to believe it shouldn't delete the structure pointer even after it passes it as a parameter to the Composite() or Prime() functions.

Or am I wrong, and it's safe to delete the structure instance after passing it to another function? Objective-C allows me to release any object in memory if I own it, provided it is part of the parent function...does VC++ care particularly much? Last thing I need is a null pointer to a structure. It's right there in my nickname :P
Was This Post Helpful? 0
  • +
  • -

#7 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 09:24 AM

View Postnullcoding, on 04 September 2012 - 06:36 AM, said:

Alright, so I'm not at my coding machine right now, but I can tell you that yes there is no ampersand there and no the LPDWORD is not initialized in the sense you mean.

So the CreateThread() call is writing to some random address that you gave it. No wonder you are getting the crashes your were seeing.
Was This Post Helpful? 0
  • +
  • -

#8 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 09:34 AM

View Postnullcoding, on 04 September 2012 - 06:36 AM, said:

Hmm so the main function (which you call DoWork) should not end the thread because it calls one of two functions depending on the results it gets. It does return 0, since it is a DWORD WINAPI function but it ought not end the thread or anything.

You're saying that if I simply have something like this:


void CalcProthPrime(pprothstructvr1)
{

// do stuff to say it's a prime number
..
..
delete pprothstructvr1

}




the thread will return/end on its own? I got the impression that beginning a thread with the CreateThread() function necessitated the use of ExitThread() as well...(as opposed to normal CRT threading etc etc)


Yes, the thread will end on its own once it get to the return statement. Highlighting the text that you quoted in your earlier post:

Quote

ExitThread is the preferred method of exiting a thread in C code. However, in C++ code, the thread is exited before any destructors can be called or any other automatic cleanup can be performed. Therefore, in C++ code, you should return from your thread function.


What the CreateThread() necessitates is calling CloseHandle() on the thread handle that you got back.

Quote

The thread object remains in the system until the thread has terminated and all handles to it have been closed through a call to CloseHandle.

from CreateThread() documentation.

Don't worry. If you call CloseHandle() before the thread is done running, the thread will continue to run. But in your case, you wanted to be able to suspend and resume threads, so you have to hold on to the handle for a while, and then only close the handle later. For that you'll have to come up some kind of scheme. Either setup a semaphore or another event so that your other thread can inform the UI thread that it is done running.
Was This Post Helpful? 0
  • +
  • -

#9 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 09:47 AM

View Postnullcoding, on 04 September 2012 - 06:36 AM, said:

I throw the pprothstructvr1 pointer around a lot...it gets passed to a lot of different functions, mostly for the sake of code cleanup and organization. I think it was you who initially told me to have helper functions instead of a single massive function rife with confusing conditionals! (glad I listened)

So essentially, the Test() function ought to return 0, but not end the thread, which leads me to believe it shouldn't delete the structure pointer even after it passes it as a parameter to the Composite() or Prime() functions.

Or am I wrong, and it's safe to delete the structure instance after passing it to another function? Objective-C allows me to release any object in memory if I own it, provided it is part of the parent function...does VC++ care particularly much? Last thing I need is a null pointer to a structure. It's right there in my nickname :P


Unless your Composite() or Prime() functions start other threads, they will be running within the same thread as the CalcProthPrime() (aka DoWork()) thread. So that structure is going to hang around as long as that thread is running. As you can see, the delete only happens towards the end of the thread.

This is no different than code that looks like this:
struct Planet
{
public:
    string Name;
    int    Distance;
};

void PrintPlanet(Planet * planet)
{
    cout << planet->Name << endl;
    cout << planet->Distance << endl;
}

int main()
{
    Planet * earth = new Planet;
    earth->Name = "Earth";
    earth->Distance = 1;
    PrintPlanet(planet);
    delete planet;
}



In the above code, you aren't worrying about deleting the planet because you passed the Planet pointer to PrintPlanet().

If you are paranoid about keeping the structure alive as long as somebody needs it, consider using the std::shared_pointer<>. It will only delete the memory when nobody is referencing the structure anymore. Beware of the pitfalls of passing a shared pointer through a void *. You'll need to play some tricks to keep the reference count alive when you "toss it over the wall" to the other thread.
Was This Post Helpful? 0
  • +
  • -

#10 nullcoding  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 99
  • Joined: 08-July 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 10:49 AM

Okay. I am trying to implement Event listeners (that's what I assume these are the equivalent of?) - I have used them in Java, though admittedly not to great effect.

I'd love to get this working but Visual Studio "stops working" every five minutes, so it's getting a bit tedious trying to type out code. Thanks for the CloseHandle() thing by the way. I was right that ExitThread() is not what I should do at all, but forgot to delete the handle! That was probably causing some problems.

Oh and the handle is actually properly initialized. I got the thread handle and threadID mixed up. It goes

HANDLE hThread = CreateThread( blah, 
blah, 
stuff,
other stuff,
another argument,
threadID);



So I guess it's fine. The suspend and resume works but the stopping appears to be really rough on the program and the environment. I'll get it eventually.
Was This Post Helpful? 0
  • +
  • -

#11 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 11:27 AM

View Postnullcoding, on 04 September 2012 - 10:49 AM, said:

Okay. I am trying to implement Event listeners (that's what I assume these are the equivalent of?) - I have used them in Java, though admittedly not to great effect.

No, these are not event listeners. They are just another standard interprocess mechanism. Think of the event created by CreateEvent() as a semaphore of range 0 to 1, where as the regular semaphore has a larger range.

If you want events in C/C++, just use plain old function pointers to do callbacks.

View Postnullcoding, on 04 September 2012 - 10:49 AM, said:

I'd love to get this working but Visual Studio "stops working" every five minutes, so it's getting a bit tedious trying to type out code. Thanks for the CloseHandle() thing by the way. I was right that ExitThread() is not what I should do at all, but forgot to delete the handle! That was probably causing some problems.

This is why I don't use VS2008 or VS2010 as my primary editting environment. :) I'm giving VS2012 a chance, but I'm keeping my SlickEdit running on standby if VS2012 starts exhibiting the same random hangs/crashes.

Try doing a "Repair" from within Add/Remove programs. It's usually a good "fix" for a couple of weeks in my past experience before the random hangs/crashes come back.

View Postnullcoding, on 04 September 2012 - 10:49 AM, said:

Oh and the handle is actually properly initialized. I got the thread handle and threadID mixed up. It goes

HANDLE hThread = CreateThread( blah, 
blah, 
stuff,
other stuff,
another argument,
threadID);



So I guess it's fine. The suspend and resume works but the stopping appears to be really rough on the program and the environment. I'll get it eventually.


I was actually asking about the threadID. Again it looks like you are missing an ampersand. Do you have:
DWORD * threadID;


or
DWORD threadID;



If the former, what is threadID pointing to? If the latter, where is the ampersand?
Was This Post Helpful? 0
  • +
  • -

#12 nullcoding  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 99
  • Joined: 08-July 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 11:37 AM

I have

// header

DWORD threadId;

// source

	pprothstructv3->Thread3=CreateThread(0,
		0,
		TestProthV3,
		pprothstructv3,
		0,
		pprothstructv3->ProthThread3);




time to try ampersands I assume...

Yeah. I'm a computer technician by trade, so I'll probably end up messing with it quite a bit until it's finally fixed/totally broken.
Was This Post Helpful? 0
  • +
  • -

#13 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 04 September 2012 - 11:41 AM

View Postnullcoding, on 04 September 2012 - 11:37 AM, said:

I have

// header

DWORD threadId;

// source

	pprothstructv3->Thread3=CreateThread(0,
		0,
		TestProthV3,
		pprothstructv3,
		0,
		pprothstructv3->ProthThread3);




time to try ampersands I assume...

Yeah. I'm a computer technician by trade, so I'll probably end up messing with it quite a bit until it's finally fixed/totally broken.


Still the same issue:
struct prothstructv3
{
    :
    DWORD * ProthThread3;    // or LPDWORD ProthThread3
};


or
struct prothstructv3
{
    :
    DWORD ProthThread3;
};



If the former, what is it pointing to? If the latter, where is the ampersand?
Was This Post Helpful? 0
  • +
  • -

#14 nullcoding  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 99
  • Joined: 08-July 12

Re: Safely managing threads from another thread (WinAPI)

Posted 05 September 2012 - 06:34 AM

Again, not at my coding machine, but to the best of my memory, it is DWORD * and the ampersand is in the right place.

At any rate, I DO NOT get any access violations anymore. Yesterday I didn't get very much accomplished because a) I was working via TeamViewer and B) VS2010 kept randomly crashing. Good thing I have a habit of saving after just about every word I type.

Also to the best of my memory, stopping the thread doesn't quite work, though pausing/resuming does.

It's been a rough couple days for me and IDEs. First VS2010 starts being finicky and then I tried to use XCode 4.4 for updating the Mac version of this same application...needless to say, it put up quite the fight!
Was This Post Helpful? 0
  • +
  • -

#15 Skydiver  Icon User is online

  • Code herder
  • member icon

Reputation: 3628
  • View blog
  • Posts: 11,320
  • Joined: 05-May 12

Re: Safely managing threads from another thread (WinAPI)

Posted 05 September 2012 - 10:11 AM

View Postnullcoding, on 05 September 2012 - 06:34 AM, said:

Again, not at my coding machine, but to the best of my memory, it is DWORD * and the ampersand is in the right place.


If it's a DWORD * ProthThread3, but you don't initialize that member to point to a DWORD prior to calling CreateThread() you'll get random access violations depending on what the random values happen to be in the memory block you get when you allocate your structure.

Do you even need the thread ID? The thread ID is an optional out parameter of the CreateThread() API.

View Postnullcoding, on 05 September 2012 - 06:34 AM, said:

Also to the best of my memory, stopping the thread doesn't quite work, though pausing/resuming does.

You have to do the check for the stop event in the right place. It does you no good if your structured like:
// main outer loop to step through test values
for(...)
{
    :
    if (stop event)
        break;
    :
    // long running loop to test one value
    for(...)
    {
        :
    }
}



The check should be in the inner long running loop.

This post has been edited by Skydiver: 05 September 2012 - 04:48 PM

Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2