First time using threads. Am I doing it correctly?

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

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 on other sites
- 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 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 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.

Share on other sites
Quote:
 Original post by mind in a boxWhen 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 on other sites
Good to know.

Did I understand the Critical Section right?

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

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 on other sites
Quote:
 Original post by mind in a boxGood 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 codeCRITICAL_SECTION cs_for_g_Health// thread Awhile(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 on other sites
Quote:
 Original post by mind in a boxA 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 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 on other sites
Quote:
 Original post by mind in a boxSo, 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.

1. 1
2. 2
JoeJ
17
3. 3
4. 4
5. 5
frob
11

• 13
• 16
• 13
• 20
• 12
• Forum Statistics

• Total Topics
632178
• Total Posts
3004608

×