Sign in to follow this  
Climax777

Multithreading phenomenon

Recommended Posts

Hey. I’ve been experimenting on a multithreaded engine. It works on the following principal: I have a main thread, which handles the message loop etc. It also runs the kernel object which has a list of ‘tasks’, which it updates every frame. The idea is to have one ‘main’ task which syncs per frame with my ThreadedExecutor(s) Then I have a system I call ThreadedExecutor, which is in essence the same as my kernel, which has a list of tasks, but it runs on a single thread per ThreadedExecutor. This is an excerpt from my ThreadedExecutor procedure, of which I currently use 2 instances:
// Loop till the kernel notifies a close event
while(WaitForSingleObject(this->m_hCloseEvent, 0) != WAIT_OBJECT_0)
{	
// Tell the main thread task that I'm ready to work
SetEvent(this->m_ActiveTaskList[0]->hTaskSyncEvent);
// Wait for main thread to acknowledge 
	// (ie. main thread task iteration is complete)
	WaitForSingleObject(this->m_hSyncStartHandle, INFINITE);
	// Tell main thread were working
	ResetEvent(this->m_ActiveTaskList[0]->hTaskSyncEvent);
	// Call all active tasks' Update function
	numActiveTasks = this->m_ActiveTaskList.GetCount();
for(this->m_uiCurrentTask = 0; this->m_uiCurrentTask < numActiveTasks; this->m_uiCurrentTask++)
	{
		this->m_ActiveTaskList[this->m_uiCurrentTask]->pTask->Update();
	}
// Wait for main thread to tell us it's done with its work
	// I'm not sure if this is needed, but doesn't work without it
	WaitForSingleObject(this->m_hSyncEndHandle, INFINITE);
}

This is an excerpt from my Main Thread procedure’s main task:
// Wait for threads to finish with previous task
if(WaitForMultipleObjects(2, &handles[0], true, 0) == 0)
{
	// Tell threads I'm not done anymore
	// Maby not needed, but it doesn't work without this
	ResetEvent(handles[3]);
	// Tell threads they can start working
	SetEvent(handles[2]);
	//WHY DOESN'T IT WORK WITHOUT THIS LINE
	_kbhit();
	//OR THIS LINE
	//Sleep(1);
	//Do rendering or whatever
	//	...
	// Tell threads to stop working when they're done
	ResetEvent(handles[2]);
	// Tell threads I'm done with a frame
	SetEvent(handles[3]);
}

For some unknown reason my threads don’t sync if I don’t call _kbhit() or Sleep(x). I checked the kbhit code, and found these lines to do the same as _kbhit():
GetNumberOfConsoleInputEvents(console, &num);
events = new INPUT_RECORD[num];
PeekConsoleInput(console, events, num, &num);
SAFE_DELETE_ARRAY(events);

What in these functions cause this phenomenon? If I don’t do any of these fragments it doesn’t sync. WHY?? I’m sure there are better ways to sync threads also. Any ideas please? Thanks [Edited by - Climax777 on December 31, 2007 5:41:43 PM]

Share this post


Link to post
Share on other sites
Sounds like you have a single threaded processor and one thread is taking all of the resources. 'source' in square brackets will put your source into nice boxes making it mildly more legible...

Share this post


Link to post
Share on other sites
Quote:
Original post by Telastyn
Sounds like you have a single threaded processor and one thread is taking all of the resources. 'source' in square brackets will put your source into nice boxes making it mildly more legible...


I have a dual core AMD Athlon 64 X2 4200+. I've monitored the cpu usage and I assure you it's not the problem. It doesn't matter what work load I put on the cores or threads.

The only thing is that if I dont put in that line (_kbhit) or (Sleep()) it doesn't sync.

But when it syncs I get amazing performance.

What can I do?

PS I hope the code is better :P

Share this post


Link to post
Share on other sites
I don't know how your code is supposed to behave, but this bit is suspect (kbhit and comments removed for clarity):

SetEvent(handles[2]);
ResetEvent(handles[2]);




There should be some kind of delay between them so that the other threads have a chance to see the SetEvent. any piece of code can work as a delay but then you never really know if your timeslice will end at just the right moment. Probably this means you need to change your architecture slightly.

[Edited by - wendigo23 on December 31, 2007 6:09:16 PM]

Share this post


Link to post
Share on other sites
Quote:
Original post by wendigo23
I don't know how your code is supposed to behave, but this bit is suspect (kbhit and comments removed for clarity):
*** Source Snippet Removed ***
Should it be set or reset? It can't be both.


The idea is that the code inbetween has to be longer than the WaitForSingleObject() in the ThreadedExecutor. That way there can't be any unwanted prestarts, before the end of the current frame. The last handle(handle[3]) is a block used to ensure that the threads don't continue until the main thread has started again.

Let me restate my logic:

When considering my main thread - The first thing is I wait for the threads to finish processing. When the if statement gets a true, I reset the event that indicates that the main thread is finished. This imples that the main thread is going to work.

The second event I SET, is the event telling my threads that they can start working.

Then my main thread should do some work (and _kbhit) and then reset the event letting my threads know they shouldn't continue working after I set the last event, which tells my thread that the frame is done.

When considering the ThreadedExecutioner:

The if is only there to test if the thread should close and is not used for syncing.

Then the event is set telling the main thread that this one is ready. Then it waits for the main thread to acknowledge (handles[2] in the main thread).

Then the event is reset, indicating that the thread is going to work now.

Then the thread works and waits for the final sync event (handels[3] in main thread.

Share this post


Link to post
Share on other sites
Quote:

The idea is that the code inbetween has to be longer than the WaitForSingleObject() in the ThreadedExecutor

That's your problem. You shouldn't rely on any kind of magic timing behavior. Perhaps the other thread should Reset it after it has successfully waited.

Share this post


Link to post
Share on other sites
Quote:
Original post by wendigo23
Quote:

The idea is that the code inbetween has to be longer than the WaitForSingleObject() in the ThreadedExecutor

That's your problem. You shouldn't rely on any kind of magic timing behavior. Perhaps the other thread should Reset it after it has successfully waited.


I tried that, but I noticed that sometimes the two worker threads would miss the set event, because they have different work loads.

But the timing isn't the issue. If I put a long workload in the main thread it still doesn't sync. But no matter the size of any workloads, it syncs perfectly if I use _kbhit

Share this post


Link to post
Share on other sites
No number of semaphores, mutexes, critical sections, or any other threading primitive can ensure that a multithreaded program will work correctly. The only way to make it work is to actually stop and look at what the threading primitives are doing and how you want the program to behave.

The cause of the current problem is that setting an event does not immediately cause threads waiting for that event to stop waiting. They will only stop waiting when they get a timeslice to run and can see that the event is set. Sleep or the kbhit (which has to wait to synchronize with the console) cause the main thread to give up its timeslice, giving the other threads a chance to run while the event is set (although it is not strictly required that the scheduler always give them this chance).

Your second problem, the "not sure if this is necessary but it doesn't work without it" event, is a temporary fix just like the sleep or kbhit. If the executors did not wait for this event, they'd loop around, see that the start event is still set, and loop around for another iteration. The start event is still set because while the executor is running the main thread is not. The main thread may still be waiting to return from the kbhit, so the start event was never reset.

Maybe you're more familiar with java's threading model, where waiting threads immediately stop waiting when the object they were waiting on is signaled? Or in any case, that seems to be the way you're trying to use the events. In windows programming, this corresponds to critical sections and condition variables (all the way down to weirdness like threads occasionally walking up even when not told to, requiring a little extra care when using them); they are designed for use within a single process, and may be more efficient than events (which can be used for interprocess communication).

Anyway, the way you're trying to use the events, it may be better to not have the events threads reset the same events that they set. The thread waiting for the event should reset it, so that way when it gets back to the wait statement, the event will not be set unless some other thread set it. Giving each executor its own "frame start" event (instead of making them share) may simplify things; you can then configure the events to automatically reset when a thread is triggered by them, eliminating the need for explicit resets.

[Edited by - Vorpy on January 1, 2008 12:58:53 PM]

Share this post


Link to post
Share on other sites
Thanks Vorpy, it makes sense. I also didn't like the fix. I like stability.

So back to the drawing board hey? Thanks.

PS what are your thoughts on OpenMP? For ease of implementation it gets my vote, but I haven't tested for performance. I see games such as doom used it.

Share this post


Link to post
Share on other sites
Eh? The fix is to just use the threading primitives correctly. There's no instability involved (and the possibility of spurious wakeups when using critical sections is easily dealt with, as demonstrated in the code samples on msdn, or it can be completely avoided by sticking to the event objects).

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