Sign in to follow this  
Chris Reynolds

Exiting from multithreaded application

Recommended Posts

In my application, I have a few threads running that can receive messages to exit the program (like GUI, window message handler, etc.)


What's the most efficient (and safe) way for a thread that receives an exit message to inform all other threads to exit? Then how do you find out in the main thread that this has been done so the application can then exit?

(Application is exclusively Windows using c++)

Thanks for any help!

Share this post


Link to post
Share on other sites
That depends on what the other threads are doing. If they're spending most of their time waiting on events, you could set an event (SetEvent()) to signal the thread should exit, or if they're constantly doing work, you could set a global volatile bool which the other threads could check in their main loop.

As for checking then they've exited, the thread handle you get back from CreateThread becomes signalled when the thread exits, so your main thread could signal them all to exit, and then WaitForMultipleObjects for them to quit, then exit itself.

Share this post


Link to post
Share on other sites
Quote:
Original post by Evil Steve
If they're spending most of their time waiting on events, you could set an event (SetEvent()) to signal the thread should exit


Since you are still checking against the return of something like WaitForSingleObject in every thread (to see if the exit event has been signaled), isn't this basically the same as a global boolean?

Share this post


Link to post
Share on other sites
As mentioned it depends on what the threads are doing; in a loading system I wrote I was using the native windows message queue (via GetMessage) to sleep threads until they got a message to do 'something' at which point the thread would check to see if it was an 'exit' message and die accordingly.

The main thread would issue the quit message and then wait for everyone to be done.

Ideally no one but the main thread should be telling other threads to exist, so if a thread decides that the app needs to exit for some reason you should really bounce it to the main thread for correct processing.

Share this post


Link to post
Share on other sites
Oh, I understand. So using SetEvent is just a way for threads to efficiently sleep while still being able to execute an exit. Interesting... unfortunately my window message handler and GUI threads are running consistently so it looks like that wouldn't offer much to me.

Thanks for the help

I ran across some advice that I think would fit into a system nicely. Calling QueueUserAPC with a handle to the targeted thread and a function that throws an exception. This way the thread exits cleanly (e.g. destructors are called). So I was thinking of storing handles to all threads created in the main thread and polling a flag that can be set by any thread for exiting. When this flag is signaled, the main thread can then call QueueUserAPC on all threads and wait for them to be destroyed before exiting. At least in my mind this seems like it would work well and would appropriately shut down 3rd party APIs running on different threads that need destructors to be called.

[Edited by - Chris Reynolds on December 1, 2010 11:19:14 AM]

Share this post


Link to post
Share on other sites
The problem with APCs is that you need the thread to be in an 'alertable' state (SleepEx, various 'wait' functions) before the APC can run.

If your thread never goes 'alertable' then it will never run the APC.

I'm also not sure that throwing an exception is a sound plan, I was under the impression that if they were thrown and not caught the thread would just terminate without any destructors/clean up code being run.

Share this post


Link to post
Share on other sites
Would SleepEx(0, true) have much overhead to put occasionally in threads? I'm not sure...

And I'm pretty sure unhandled exceptions unwind the call stack.

Anyway, I'm not sure that this is a very good method anyway :\ I'm new to the parallel scene

Share this post


Link to post
Share on other sites
Problems such as these should have handled at the highest level possible and not inside of child threads, that is unless these child threads are continually running and de coupled from the main thread. If that is the case, place a check at the beginning of the work to be done. Do not try placing fine-grained checking, this will only slow down your code and you will get nothing in return. So what if the thread exits .005 ms faster? If you do the checks ones per game loop, you are good to go. Forgo the complicated, fine grained checking -- its usually never needed.

Share this post


Link to post
Share on other sites
Quote:
Original post by Chris Reynolds
Would SleepEx(0, true) have much overhead to put occasionally in threads?
Funnily, this is MUCH faster than pretty much anything else, at least as far as the legitimate, documented functions go. The function that receives messages and make the thread alertable (I forgot its exact name, but you know which one I mean) is a terrible lot slower than calling GetMessage followed by SleepEx(0,1). Plus, the former does some weird stuff sometimes.

You can manually load NtTestAlert which is only a few dozen cycles overhead, so basically zero. But of course it's undocumented, so... evil.

In genereal, I've found user APCs a nice thing (being much faster than posting a thread message, for example), but completion ports are far superior in every respect (faster, and more straightforward, easier to use).

I keep a count of the number of worker threads and post N "please quit" messages on the IOCP, then do a WaitForMultipleObjects on the array of thread handles.
The completion port will wake up one thread after the other and pass the "please quit" message to it, after which it will just return from its thread function. It can't be much easier and safer than that. Well, a global boolean would probably be easier, but hey, we're trying to communicate something useful during the program's lifetime, too.

Share this post


Link to post
Share on other sites
@samoth - That sounds interesting. Could you post a brief example of this? I'm not sure what posting to the IOCP or how a thread would react to this message would look like. It would greatly appreciated, I feel like I've jumped into the deep end with this parallel stuff.

Share this post


Link to post
Share on other sites
You call GetQueuedCompletionStatus, which gives you back a DWORD value, a ULONG_PTR, and a OVERLAPPED*. If you use IOCP for asnyc file IO at the same time, you'll get the number of bytes read in the DWORD value. I use small negative values (which are in fact insanely large positive values, unsigned) to communicate my "commands". The ULONG_PTR can be anything, it could for example point to a user struct that holds everything your "command" needs. However, if you do async IO, it is the "key" that you assigned with a certain handle. I don't really like this much, since you can have only one key per handle, so I'm not using that.
Instead, I have a union that can be cast to an OVERLAPPED pointer, which either contains the OVERLAPPED needed by the file functions plus any additional data that I want, or just some data that my commands consume. This works nicely for everything, no special paths and no special handling for file functions.

A command where both lenght and pointer are zero means "goodbye". This does not normally occur anywhere (you can have zero length reads reported by async IO, but the handle should never be zero). You can of course choose anything else, but I think zero-zero is comprehensive and good.

So, my thread entry function looks about like this:

for(;;)
{
if(GetQueuedCompletionStatus(iocp, &n, &k, (OVERLAPPED**)&o, INFINITE) == 0)
{
if(GetLastError() == ERROR_OPERATION_ABORTED)
{
// you get this if you cancelled async IO
}
else
{
if(!HandleFailure(o)) return; // get this for various failures
}
}
else
{
if(n == 0 && k == 0 && o == 0) // cancellation message
return; // this terminates the thread

switch(-n)
{
// otherwise do something else, whatever the command be
// ...
}
}
}




You use PostQueuedCompletionStatus to post messages to the IOCP. If your main thread wants to exit, do PostQueuedCompletionStatus(iocp, 0, 0, 0) once for every worker, and they'll all go away.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this