PDA

View Full Version : Multithreading / WaitForSingleObject


Wernaeh
10-15-2005, 01:07 PM
Hiho there =)

Just a few small questions today that I came up with when working on a
multithreaded worker / overseer implementation =)

I have a situation where in a windows environment one thread first rises a
flag indicating a second thread should terminate. Then, the first thread calls
Resume() on the second thread - just in case it might be suspended - and
then calls WaitForSingleObject(secondthread). The second thread runs in a
loop, and is testing for the exit flag in each cycle.

i.e. in pseudocode



// The second thread's main loop
void ThreadLoop
{

while (true)
{

// Lock all data used by this thread
SetMutex();

// See if we should terminate.
// If so, fall out of the thread loop
if (bExitFlag)
{
ReleaseMutex();
return;
}

// Otherwise, do our working stuff, and then
// release the mutex again.
DoSomeStuff();
ReleaseMutex();

// Finally, start sleeping so we won't cost any cycles
Suspend();
}
}

// The first thread's function to terminate the second thread
void TerminateOtherThread()
{
// Set the exit flag for the other thread
SetMutex();
bExitFlag = true;
FreeMutex();

// Now, revive the other thread in case it is suspended,
// and then, wait for its termination
ResumeThread(secondthread);
WaitForSingleObject(secondthread);

// Release remaining thread API stuff
CloseHandle(secondthread);
}



So now, on to the questions that popped into my mind.

May this setup lead to a deadlock ? - I think so.

Why ? Because ResumeThread() may be called before the second thread is
actually suspended, thus doing nothing, then the second thread Suspends(),
and finally the first thread waits for a suspended second thread, so thats a
no-no. Any workaround solution - except perhaps putting a mutex around the
suspending command ?

Second, is WaitForSingleObject terminating when the specified process _is_
terminated, or when it has _been_ terminated ? The MSDN is a little unclear
about this. So if my second thread ends before my first thread actually
reached the WaitForSingleObject() call, will WaitForSingleObject return as it
should?

The entire setup represents a rendering thread within a larger physics
simulation. Is suspending the rendering thread even a good idea
performancewise if we are aiming at 30 frames or so per second ?

And finally, do I need to set a mutex if I just change a single boolean
variable ? I guess I needn't, since the variable set operation should be
atomar anyways.

I'm not too experienced in this whole multithreaded business, so I'd be glad
about any input. =)

Thank you for you help and time,
Cheers,
- Wernaeh

MJeannig
10-16-2005, 03:03 AM
This is not very good pratice to communicate with your thread using variables. I'm not sure about what does your FreeMutex, so I may be wrong. One better way is to use an controlled loop with UserAPC (user asynchronous proc ). These are function called in the target thread context while it is in alertable state (SleepEx, WaitForSingleObject, ... )

MyThread()
{
while(WaitForSingleObject(exitEvent) == WAIT_IO_COMPLETION);
}

MyProc()
{
//Do something in MyThread context
}

MyApp()
{
QueueUserAPC(hMyThread,MyProc); // run some code in MyThread context
SetEvent(exitEvent); // tell the thread to close
}

Or you may keep a mutex/event solution with a two event solution using waitformultipleobject:
MyThread()
{
do
{
... // Do something
HANDLE handles[] = {exitEvent,continueEvent}
r = WaitForMultipleObject(handles)
}while(r == 1+WAIT_OBJECT_0); // 1 is index of continueEvent
}
MyApp()
{
SetEvent(continueEvent); // Make it continue
SetEvent(exitEvent); // Make it stop
}



MJ

MJeannig
10-16-2005, 03:16 AM
The entire setup represents a rendering thread within a larger physics
simulation. Is suspending the rendering thread even a good idea
performancewise if we are aiming at 30 frames or so per second ?

Its better to not use thread if you can (except may be for multiprocessor optimization purpose ). Thread are for accessing hardware, or deal with long operations that could deteriorate user experience. Suspending the rendering thread is probably not a good idea. Do you have separate threads for physic and rendering ?

And finally, do I need to set a mutex if I just change a single boolean
variable ? I guess I needn't, since the variable set operation should be
atomar anyways.

Its a dangerous field to try to guess what is atomic and what isnt. It will probably work 99.9999% of the time and it would be a hard debugging time if it cause crash. You will never know if your engine is really stable.

corey
10-16-2005, 10:34 AM
If you're really worried about it and must suspend the thread instead of sleeping for a period of time then code with that in mind.

Instead of calling a direct suspend, setup an event object and wait for that to be signaled. Here, you could use wait for multiple objects and check for either resume event or close event.

Use a mutex for any synchronization. Windows does provide some atomic manipulation functions if you're interesting. We're using them in G3D's new AtomicInt32 and GThread classes. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/interlockedexchange.asp

corey

Wernaeh
10-26-2005, 02:03 PM
Hi there again =)


Sorry for the response lag, I had a lot to do the past two weeks with the new university semester starting, and such =)

First, thank you all for your helpful posts =)
Now, just a few comments, and answers to questions.


@MJeannig

I'm not sure about what does your FreeMutex

SetMutex() and FreeMutex() are just wrappers for OS specific calls that either occupy or release a mutex object (or a critical section object, in my case)

I didn't completely understand APCs though I read the msn about it. It would certainly be nice if you could detail a bit on their application and such =)
However, I think your event-based approach is a very good solution to my problem, indeed. I will try it out right after this post.

Its a dangerous field to try to guess what is atomic and what isnt.It will probably work 99.9999% of the time

Yes that's exactly what I was worried about ;)


@corey

Do you have separate threads for physic and rendering ?

Yes I do. I have got a fixed timestep logic thread (also doing all physics) and the rendering thread. I also have plans for another loader thread running in the background.

There are good alternatives to threading, I know. One example would be to just break off rendering after either a fixed amount of objects rendered, or after a finite time passed (i.e. its time for the logic again). Then, do a logic step without rendering, and continue rendering on the previous frame where you left.
This is a bit tedious since you need to explicitly code time checks all over your rendering code, and you need to somehow store where you left rendering. Additionally, this approach requires two different versions of each object - the one manipulated by logics, and the one rendered, to avoid scene inconsistencies. However, the latter also is a necessity for threaded rendering, if you don't want to mutex each and every single object.

Another alternative would be to just have the logic catch up with rendering. This means, render a frame, then see how many logic frames should have passed, and process these. Then render once more. I don't fancy this approach though, since it gives very inresponsive logics (i.e. wenn the rendering of a frame kicks in, you basically lose input for some time, and then get very fast input for repeating the logic steps).

This also explains the reason for using a threaded approach. Apart from running faster on multicore CPUs, the basic coding is much simpler and reliable, once you have a proper synching system in place.


Anyways, I had some time to do some work on my threading stuff the past few hours (... boring economy lecture courses ...) . I think I finally came up with a solution which just uses mutexes. It is ugly as hell though, and I'm pretty much too embarrassed to post it here ;)

For anybody out there trying something similiar, either don't suspend your worker thread, or use events, as suggested by the poster above, it gets much, much easier that way.

Thank you all again for your answers.

Cheers,
- Wernaeh

corey
10-26-2005, 02:16 PM
Wernaeh,

I would probably still suggest my approach for a clean solution and readability.

If you do post, we can try to help iron out some clarity or other issues if you like.

Corey

Wernaeh
10-26-2005, 02:22 PM
As I said, I will try out the event-based approach right now ;) as it can probably never be as ugly as the one I devised, with using critical sections ans synch variables all over ;)

corey
10-26-2005, 05:54 PM
As I said, I will try out the event-based approach right now ;) as it can probably never be as ugly as the one I devised, with using critical sections ans synch variables all over ;)
Well, it might not be a problem for you, but if you're supporting any windows version below NT, there is no tryLock() for critical sections! That is usually an easy way to simulate events -- but then again you wanted to suspend the thread.

corey