Jump to content
  • Advertisement
Sign in to follow this  
mind in a box

First time using threads. Am I doing it correctly?

This topic is 2865 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi there!

I've implemented a test-thread into my game. Just to see how those things work. Now I would like to know if I have done this all correctly. Code wise. The thread seems to work properly.

Note that this is a very simple example, without any "Mutexes" and the other safety stuff I read about. This comes later [smile]




struct TestThreadWorkStruct
{
//This class calculates the Frames per second (Save to leave it public)
CTimer ThreadFPS;

//Just to make things cleaner
void StopThread()
{
bStop=true;
}

//Init variables
TestThreadWorkStruct()
{
bStop=false;
}
private:
//Should the thread stop?
bool bStop;
};

TestThreadWorkStruct TestThreadWork; //Work for the test thread


...


int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
srand((unsigned)timeGetTime());

MainHWND=CreateDialog(hInstance,L"IDD_Game",NULL,(DLGPROC)GameDlgProc);

registerMouse();

Scene.InitScene();
BlockPlacer.Scene=&Scene;





//Create a thread for testing
HANDLE TestThread;
DWORD TestThreadID;


TestThread = CreateThread( NULL,
0,
ThreadFunc,
(LPVOID)&TestThreadWork,
0,
&TestThreadID
);





MSG msg;
while(g_bWantsQuit==false)
{
bool bGotMsg;
// Use PeekMessage() so we can use idle time to render the scene.
bGotMsg = ( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) != 0 );

if( bGotMsg )
{

TranslateMessage( &msg );
DispatchMessage( &msg );

}
else
{
RenderScene();
}
}


//Tell the thread to get finieshes with it's work
TestThreadWork.StopThread();

//Wait until the test thread has finished the work
WaitForMultipleObjects( 1,
&TestThread,
TRUE,
INFINITE );

CloseHandle(TestThread);

...
}

...

DWORD WINAPI ThreadFunc(LPVOID Data)
{
TestThreadWorkStruct* Work=(TestThreadWorkStruct *)Data;
Work->ThreadFPS.Start();

while(Work->bStop==false)
{
Work->ThreadFPS.Update();
}

Work->ThreadFPS.Stop();
return 1;
}



(I changed the order of the code pieces a bit)

In my HUD-Render-Function (in the main-thread) I simply do

float TestFPS=TestThread.ThreadFPS.GetFPS();


Can I do it like this? I mean, have a struct with data which tells the thread what to do? And then receive the data from it in another thread?

Share this post


Link to post
Share on other sites
Advertisement
- Make bstop volatile.
- WaitForSingleObject, there is only one thread.

Quote:
I mean, have a struct with data which tells the thread what to do? And then receive the data from it in another thread?

As long as data is properly synchronized, then yes. For single integer value, one of InterlockedXYZ functions can be used to update the value.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
- Make bstop volatile.


To the OP, beware that the solution suggested by Antheus is not portable. The semantics of 'volatile' are compiler-specific. In all Visual Studio versions since 2005, volatile actually works for this type of scenario, but it doesn't in older Visual Studio versions, and there may very well be other Windows compilers (not to mention other operating systems and platforms) where it doesn't either.

In general, 'volatile' is completely unsuitable for use in multi-threading and should never be used except for its original intended purpose. The safe and portable way would be to use critical sections / mutexes to synchronize access to any data shared across threads.

Also, I recommend using boost::thread instead of using your operating system's threadig API directly. It's become pretty much a standard these days, plus its use of scoped locks (letting RAII handle the acquisition and release of mutexes implicitly) is infinitely superior to having to LeaveCriticalSection() manually (and probably forgetting to do so in a few places, which creates potential for deadlocks and other fun bugs).

Share this post


Link to post
Share on other sites
Ok I'm a bit confused right now.

Quote:

For single integer value, one of InterlockedXYZ functions can be used to update the value.

I don't get what the MSDN says: What is a Atomic operation in fact? It updates a value, but how exactly? When I call this function to update a value, what happens when another thread wants to read/write this value?


Quote:

Also, I recommend using boost::thread instead of using your operating system's threadig API directly. It's become pretty much a standard these days, plus its use of scoped locks (letting RAII handle the acquisition and release of mutexes implicitly) is infinitely superior to having to LeaveCriticalSection() manually (and probably forgetting to do so in a few places, which creates potential for deadlocks and other fun bugs).


I heard of the boost::threads. But for now I just wanted to get a simple thread working. If it's better I will switch to them.

Threading is confusing. [grin]

Share this post


Link to post
Share on other sites
Quote:
Original post by mind in a box
When I call this function to update a value, what happens when another thread wants to read/write this value?



Naturally, any thread trying to access that data must use one of the interlocked functions, too (otherwise the result is undefined). If all threads stick to that rule, the implementation of the interlocked functions makes sure there are no inconsistencies.

Share this post


Link to post
Share on other sites
Good to know.

Did I understand the Critical Section right?

Thread A:

while(1)
{
BeginCriticalSection
g_Health*=5;
g_Health=DoMoreFoo(g_Health);
LeaveCriticalSection
}


Thread B:

while(1)
{
Draw(g_Health);
g_Health=20;
}


will I be able to read from g_Health in the draw call when I am in the critical section? Or will ThreadB stop and wait until ThreadA has finished the section? Or will that happen when ThreadB writes to g_Health?

A critical section stops other threads from accessing variables in them, don't them?

If not, what technique should I use if I have such a scenario?

Share this post


Link to post
Share on other sites
Quote:
Original post by mind in a box
Good to know.

Did I understand the Critical Section right?

Thread A:

while(1)
{
BeginCriticalSection
g_Health*=5;
g_Health=DoMoreFoo(g_Health);
LeaveCriticalSection
}


Thread B:

while(1)
{
Draw(g_Health);
g_Health=20;
}


will I be able to read from g_Health in the draw call when I am in the critical section? Or will ThreadB stop and wait until ThreadA has finished the section? Or will that happen when ThreadB writes to g_Health?

A critical section stops other threads from accessing variables in them, don't them?

If not, what technique should I use if I have such a scenario?



The problem in your little snippet is that, while thread A respects the fact that access to g_Health must be synchronized by performing any accesses inside a critical section, thread B just goes and modifies it when ever anyway. :)

For synchronization to be meaningful, any and all threads wishing to access the protected data must use a critical section. And not just ANY critical section ... it has to be the exact same critical section, too.


// pseudo code

CRITICAL_SECTION cs_for_g_Health

// thread A
while(1)
{
BeginCriticalSection( cs_for_g_Health );
g_Health*=5;
g_Health=DoMoreFoo(g_Health);
LeaveCriticalSection( cs_for_g_Health );
}

//Thread B:
while(1)
{
BeginCriticalSection( cs_for_g_Health );
Draw(g_Health);
g_Health=20;
LeaveCriticalSection( cs_for_g_Health );
}



Share this post


Link to post
Share on other sites
Quote:
Original post by mind in a box
A critical section stops other threads from accessing variables in them, don't them?


Yeah, a critical section can only be 'owned' or 'held' by one thread at any point in time (after all, that's the whole point!). While one thread has acquired the critical section, any other threads also wishing to acquire it must wait. The wait ends the moment the thread currently owning the critical section releases it, at which point one of the threads currently waiting for the critical section will acquire it.

Share this post


Link to post
Share on other sites
So, everytime (When it makes sense) I have to change a important variable or I have to compute something, I have to surround them with CriticalSections?

Share this post


Link to post
Share on other sites
Quote:
Original post by mind in a box
So, everytime (When it makes sense) I have to change a important variable or I have to compute something, I have to surround them with CriticalSections?


If more than one thread could potentially access that data __and__ at least one of those threads actually MODIFIES the data (as opposed to just reading it), yes. If you have n readers and at least one writer, you need to lock. If you only have n readers, no locking is necessary.

You've probably guessed by now that this doesn't come for free. Frequent mutex locking costs performance and in extreme cases it may even totally kill any parallelism in your program, since while threads are waiting to acquire some mutex, they can't do any work. That's why, in a real world program, you want to avoid shared data as much as possible. Obviously, your threads WILL usually have to share some data, but you should keep it down to a minimum.

For instance, rather than having one shared instance of a struct and constantly locking mutexes to access that instance each time a thread needs to read or write a member of that struct, it may be better give each thread its own copy of that struct, then use that without any locking and then lock a mutex just once per game cycle to synchronize both copies of the struct with each other.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!