multithreading

Started by
13 comments, last by fuzzy 23 years, 2 months ago
I recently read an article about removing the game from the message pump and giving it it''s own thread in the programming articles. This works great, increasing the frame rate considerably among other benefits, but I''m not very experienced with using and especially debugging multithreaded programs. What I would like is to know where to learn good multithreaded techniques. If you know of any tutorial, source code, or book on the subject, it would be a great help.
Advertisement
Yeah, me too!
a.h{text-decoration:none;color:blue;};a.h:hover{text-decoration:underline;background:red;};

Why is it called a hot water heater? Isn't it cold when it goes in the tank?

[email=jtaylor@gtemail.net" class="h]-=CF=-[/email]
I haven''t found a decent source on Win32 Multithreading.
I can tell you to avoid mutlithreading & MFC - MFC is not threadsafe.

If you understood the article, go read about CriticalSections & Events in the MSDN.microsoft.com - thier in the platform SDK reference section. Look up syncronization functions.

I don''t think you''ll need mutexes or semaphores writing a game - but events are extremely useful (NO MORE POLLING!!!) and critical sections are a must for a truely multithreaded application.

If you read anything about this so-called ''apartment'' thread model, disregard it. Creating an ''apartment'' threaded applications provides much less benefit than a fully multi-threaded design.

Here''s the skeleton of a CComPort class I''m working on that uses a thread & events. It works well under W2k & NT4 - I''m told that the comm support on 95 is crap, slightly better on 98, & almost works on ME.
  class CComPort	{	public:		CComPort();		CComPort(CComPortObserver*);		~CComPort();	//Commands	public:		HRESULT Open(LPCTSTR szComPort, DWORD dwBaud, BYTE byParity, BYTE byStop);	//Worker Thread	private:		static unsigned __stdcall CommProc(void* pComPort);		HANDLE m_thrCommProc;		HANDLE m_evExit;}HRESULT CComPort::Open(LPCTSTR szComPort, DWORD dwBaud, BYTE byParity, BYTE byStop)	{	//init code...	//starts a thread!	if(!(m_thrCommProc = (HANDLE) _beginthreadex(NULL, 0, CComPort::CommProc, this, 0, &threadID)))	}unsigned __stdcall CComPort::CommProc(void* This)	{	CComPort* pComPort = (CComPort*) This;	//...does stuff here...		//Events to wait on	HANDLE WaitEvent[4];	WaitEvent[0] = pComPort->m_evExit;	WaitEvent[1] = pComPort->m_OStatus.hEvent;	WaitEvent[2] = pComPort->m_ORead.hEvent;	WaitEvent[3] = pComPort->m_OWrite.hEvent;		//Process until the m_evExit event is signaled	//Just check to see if the event is signaled, don''t wait on it here	while(WAIT_OBJECT_0!=WaitForSingleObject(pComPort->m_evExit, 0))			//As soon as any event signals, we process it			//This is truely an event driven loop  (there is no polling)			//As such there is no performance hit to run this thread at Time Critical			//Consequentially, this provides the lowest latency possible on Win32			switch(WaitForMultipleObjects(4, WaitEvent, FALSE, INFINITE))	_endthreadex(0);	}  
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
quote:Original post by Magmai Kai Holmlor
I can tell you to avoid mutlithreading & MFC - MFC is not threadsafe.

Really? Wow, I''d better get back all of my released products from my customers and tell them my entire software paradigm is incorrect. I''ve got a possible total of 8 threads running in an MFC program. It must be by pure virtue of magic that it works at all.

Seriously, don''t post disinformation like this unless you can back it up (which you can''t, because it''s not true). MFC works fine and dandy with a single thread and with multiple threads.

Also, watch out when using critical sections: they''re faster than mutexes, but you can''t set a timeout period for waiting on them. You also can''t use them with WaitForSingleObject, which makes them different than all of the other win32 synchronization objects (thread handles, events, mutexes, semaphores, etc.). Instead, you have to use Enter and LeaveCriticalSection. This means that if you want to WaitForMultipleObjects like in the above example, you can''t have a critical section be one of the objects on which you wait.

As far as answering the original question on techniques for using and debugging, I don''t have any sources. Most is learned through extremely painful trial & error. Debugging problems that come from improperly synchronized threads is one of the most difficult tasks I''ve ever had. Some basic tips, though:

- make sure you list exactly what each mutex/critical section protects, and you adhere to that definition
- callbacks to log functions in worker threads help a lot in debugging
- if you''re using MSVC++, put the "threads" control from the debug menu on your debugging menu bar--you will learn to love it. With this control you can stop all threads but one and step through it.
- if you get into a problem where debug code works but release code doesn''t (or vice versa), odds are you have a thread synchronization problem.

That''s all the general tips I can think of off the top of my head. If you have some specific questions, I can probably answer them.
MFC as is, is not mutli-thread safe, and it is stated as so in the MSDN docs. That of course, does not mean it cannot be used in a mutli-threaded application. MFC is 'apartment' thread safe - which is not mutli-thread safe at all.

The STL is not multi-thread safe either, but many people use it with mutli-threaded applications.

What do you think multi-thread safe means? When I say mutli-thread safe, I mean you can use the object in mutliple threads concurrently. Win32 is mutli-thread safe, but MFC is not because it utilizes thread local storage. Some MFC objects are mutli-thread safe, but not all most notibly CWnd.

So do tell, you have a mutli-threaded application, and you never call GetSafeHwnd?

From the horse's mouth:
quote:
As a general rule, a thread can access only MFC objects that it created. This is because temporary and permanent Windows handle maps are kept in thread local storage to ensure protection from simultaneous access from multiple threads. For example, a worker thread cannot perform a calculation and then call a document’s UpdateAllViews member function to have the windows that contain views on the new data modified. This will have no effect at all, because the map from CWnd objects to HWNDs is local to the primary thread. This means that one thread may have a mapping from a Windows handle to a C++ object, but another thread may map that same handle to a different C++ object. Changes made in one thread would not be reflected in the other.

Magmai Kai Holmlor
- The disgruntled & disillusioned


Edited by - Magmai Kai Holmlor on February 2, 2001 9:43:36 PM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
fuzzy: I advise studying multithreading, or it''ll cause more headaches than it''s worth. The basic idea behind synchronization in an OS like Windows is that the processor caches variables in its registers, the system manipulates memory addresses, etc. behind your back, so the basic idea is that you must not access the same data in two threads without synchronization.

Synchronization takes time. For multithreading to be efficient, you need a minimum of communication between the two threads. Clearly define what data must be accessed by what threads, and synchronize it.

If you don''t understand what you are doing, you can quite easily lock up your app, but that isn''t much different from braving DirectX exclusive mode anyway.
quote:Original post by Magmai Kai Holmlor

MFC as is, is not mutli-thread safe, and it is stated as so in the MSDN docs. That of course, does not mean it cannot be used in a mutli-threaded application. MFC is ''apartment'' thread safe - which is not mutli-thread safe at all.


Then why did you say, "don''t use multi-threading with MFC"? That was the statement with which I take exception.

quote:
So do tell, you have a mutli-threaded application, and you never call GetSafeHwnd?


No, I don''t. I keep UI threads and worker threads separate. The only thread that accesses each CWnd is the thread which created it. The behavior that prohibits me from doing otherwise was designed into MFC to keep calls to CWnds from colliding with each other.

You really need to think about what "thread-safe" means. Most software modules are not thread-safe; they require external synchronization. If a module has internal synchronization, it means that it has to have kernel-level synchronization objects within it. In most cases, that would be overkill. How useful would you find a string class or a linked list class that carried around a mutex that needed to be locked for every operation?

In your first post you equated "thread-safe" with "usable in multithreaded applications", and "not thread-safe" with "don''t use in multithreaded applications". This is just plain wrong.
Calm down, Stoffel... I see your point but you won''t get anywhere by coming in guns a''blazing.

MFC can be used in multithreading, but you should be aware of how multithreading and synchronization works. Inline MFC member functions that merely pass handles and local objects (i.e., pass-by-value) are generally fine because Windows synchronizes its internal code that works with handles anyway - no need to do it twice. MFC member functions that manipulate other types of class data are generally unsafe.

There should be an article in MSDN about MFC and multithreading.
I believe I used the word 'avoid' not 'impossible'.
And true, nearly all code is not thread safe, but most of it requires simple sycronization - MFC objects require construction inside the thread that they are used in, which a-mon-avis is not an intuitive step to take.
They are new to thread trashing, so as I see it, it would cause undue stress to deal with the additional threading issues of MFC. (I'll give you that it's not /that/ hard, but it seemed reasonable to me to pass CWnd* between threads when I first tried...)

If you wish to take issue with my opinion on apartment threads, then fine you're entitled to honor them as much I disgrace them. Neither opinion of which changes the thread unsafety of MFC

Frankly, of course apartment threading works, if you don't use the same functions, and don't pass any data between them - how could it not!?

quote:
How useful would you find a string class or a linked list class that carried around a mutex that needed to be locked for every operation?

Very useful if I was sharing the string between processes, otherwise I'd wonder why they didn't use a critical section.

The MFC functions are not reentrant, it's a much deeper problem than data access sycronization. If they didn't use TLS, and require you to construct additional objects for use in other threads, you would only be able to use a given MFC class in one thread - fortunetly they hacked a TLS solution so you can. I think they did it for performance reasons in the days of yore that just don't apply any more... It doesn't really matter if it takes 60us instead of 6us to resolve one msg, at the dawn of Win95, it would have been 1200us instead of 120us.

Hey null,
quote:
MFC member functions that manipulate other types of class data are generally unsafe.

But it's worse than that. If you pass a CWnd* from one thread to another, the second thread will not have access to the msg map for the WndProc... I'm not entirely certain what all the issues are, and they are notably evasive in the MSDN docs.
All I know is that I got a ton of assertion failures & crashes Can can work around it; you have to construct your CWnd in each thread you want to use it in from the native HWND. But then you're mutli-threading with Win32 handles not MFC objects.

If the only threading issue with MFC was a simple EnterCriticalSection(csMFC), it wouldn't be a big deal...

Edited by - Magmai Kai Holmlor on February 3, 2001 7:13:11 PM
- The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara
Hmph. Forgot about that one.

Thanks for reminding me!

This topic is closed to new replies.

Advertisement