This document answers some common questions concerning multithreaded programming in Visual C++, both with MFC and at the Windows API level. It's structured mainly as a conversation, in which one question and answer leads to the next, so you'll get the most out of it if you read it as a whole. To give you a quick idea of what I'm going to talk about, the questions are:
| Q1 | How can I safely use MFC's CWinThread class? |
| Q2 | Should I join with my secondary threads before exiting my program, or is it OK to let the system terminate them for me? |
| Q3 | What are the advantages to using an event synchronization object over a simple bool to ask a thread to exit? |
| Q4 | How can I ask a thread to exit and wait for it, anyway? |
| Q5 | How can I make my program wait for a thread to exit without blocking its user interface or sacrificing its ability to process messages and wait on other objects? |
| Q6 | What's the correct way to use MsgWaitForMultipleObjects? |
| Q7 | How can I control the user's interaction with my program in my MsgWaitForMultipleObjects loop? |
| Q8 | How can my thread notify another thread or a window that it has exited? |
Q1. How can I safely use MFC's CWinThread class?
A. To safely use CWinThread, you must start the thread suspended and set the CWinThread object's m_bAutoDelete member to false or DuplicateHandle a copy of its m_hThread member. Only then should you resume the thread. This avoids the race condition resulting from the default auto-delete behavior, allowing you to wait on the thread handle, something you need to do for reasons given in Q2 below. Briefly, the race condition arises because by default, a CWinThread object deletes itself when its associated thread terminates, invalidating any pointer to the CWinThread object you've kept around. Because this deletion occurs in the secondary thread, it runs asynchronously to the thread that created the CWinThread object, and unless you're very, very careful, the CWinThread pointer you kept can be invalidated while you're still using it. If that wasn't bad enough, according to the WaitForSingleObject documentation, it's undefined for a handle to be closed while you're waiting on it. Thus, all code which waits on a CWinThread object's m_hThread member while the object auto-deletes itself is undefined, because auto-deletion also closes m_hThread.
So what are the consequences of starting the thread suspended and dealing with this problem? If you set m_bAutoDelete to false, you become the owner of the CWinThread object and assume the responsibility for deleting it. On the other hand, if you duplicate m_hThread, you will have to close the duplicated handle when you're done with it. I find it easier just to change m_bAutoDelete.
Q2. Should I join with my secondary threads before exiting my program, or is it OK to let the system terminate them for me?
A. In general, it's a bad idea to allow secondary threads to continue executing as the application is shutting down. By "application", I mean the primary thread, which is the thread that initializes and destroys global data and runs the main function. (Although I'm talking about console program functions such as main, everything I say here applies to GUI programs, too.) After you return from main or call exit, the CRT takes over, destroys static duration objects, and performs other termination duties, until the CRT finally calls ExitProcess, which forcibly terminates all remaining secondary threads. The problem is, these secondary threads have continued to execute oblivious to the fact the environment in which they're running and the data they may be using are being destroyed all around them. Think Rome, Nero, and a fiddle, and you'll get the picture. To avoid this and achieve an orderly shutdown, you have to join with all your threads before exiting your program, and that means being able to wait on their handles. For CWinThread, this brings us back to Q1 and the necessity of disabling auto-deletion. This whole auto-deletion concept as applied to threads is pretty much a bad idea, with limited utility.
Q3. What are the advantages to using an event synchronization object over a simple bool to ask a thread to exit?
A. Events are much more flexible. They support polling with WaitForSingleObject, but you can also use them in WaitForMultipleObjects. This comes in handy when you'd like to wait forever on some other object but retain the ability to exit quickly when asked. You can't use WaitForSingleObject(hObj, INFINITE), because you'd miss thread exit requests that are made before the object you're waiting on is signaled. Some people whose use boolean quitNow variables solve this problem by waiting a second or two and polling their quitNow variable in a loop. But if you use an event and store it as the first object in the WaitForMultipleObjects handle array, you can wait forever, which means you wait more efficiently, and you normally don't need the while (!quitNow) loop. My code tends to look like this, where WaitAny is a convenient interface to WaitForMultipleObjects:
// WaitAny is overloaded on up to 5 WaitableHandles, and it waits forever.
// quitEvent is an Event object, and Event is derived from WaitableHandle.
switch (WaitAny(quitEvent, other WaitableHandles))
{
case WAIT_OBJECT_0 :
clean up and exit;
...other cases
}
This is considerably simpler than constructing a loop to poll a bool at regular intervals, and it's more efficient to boot, especially if you can end up waiting for a long time.
Q4. How can I ask a thread to exit and wait for it, anyway?
A. Assuming you've read everything above, the following should make sense:
// The variable pThread points to a CMyThread that was started // suspended, and whose m_bAutoDelete member was set to false, // as described in Q1. The class CMyThread was derived from // CWinThread and contains an event variable called quitEvent. SetEvent(pThread->quitEvent); WaitForSingleObject(pThread->m_hThread, INFINITE); delete pThread;
This works great if you've designed the secondary thread to exit quickly once quitEvent has been set. However, the waiting thread cannot proceed until the other thread has terminated, so this sequence should not be used unless you can guarantee a timely exit, or it's acceptable to wait indefinitely. For an MFC program's main GUI thread, it's typically not acceptable to wait indefinitely in WaitForSingleObject, because it prevents the program from processing messages, which makes it unresponsive to user input, redraw requests, and so forth.
Q5. How can I make my program wait for a thread to exit without blocking its user interface or sacrificing its ability to process messages and wait on other objects?
A. The only way is to resume a message loop, which destroys the modality of what you're currently doing. You must account for this change and keep your program in a good state. For example, you may have to guard against creating another thread until the current one finishes. You'll have to deal with the user trying to quit your program while the thread is running. And so on. Once you've accounted for all these things, your primary thread still needs to join with the secondary thread and delete the CWinThread object when the thread has terminated. The easiest way is to do this in response to a custom message posted from the secondary thread to a window belonging to the main thread; you would typically use PostMessage for this. In some cases, you might prefer to wait on the thread handle in a MsgWaitForMultipleObjects loop, because it lets you vet the incoming messages locally, which relieves you from modifying your main message loop, which in many applications, isn't readily available to modify, because it's buried deep inside a library like MFC. If you do return to a global message loop, you'll have to maintain a flag that other parts of your program can inspect, so they can decide how to process a given message while your program is in this waiting for a thread to exit state.
Q6. What's the correct way to use MsgWaitForMultipleObjects?
A. First, you have to understand the new input issue. The documentation says:
MsgWaitForMultipleObjects does not return if there is unread input of the specified type in the message queue after the thread has called a function to check the queue. This is because functions such as PeekMessage, GetMessage, GetQueueStatus, and WaitMessage check the queue and then change the state information for the queue so that the input is no longer considered new. A subsequent call to MsgWaitForMultipleObjects will not return until new input of the specified type arrives. The existing unread input (received prior to the last time the thread checked the queue) is ignored.
In a nutshell, unless you're certain of everything that preceded the MsgWaitForMultipleObjects call, you need to process any queued messages in a PeekMessage loop before dropping into MsgWaitForMultipleObjects. Otherwise, MsgWaitForMultipleObjects might not wake up until additional input becomes available, which can take an indefinite amount of time, even though there is a message waiting for your application at the time you called MsgWaitForMultipleObjects. Here's a sketch of a MsgWaitForMultipleObjects loop for MFC; it can be slightly simplified by using MsgWaitForMultipleObjectsEx, but then you sacrifice Windows 95 compatibility:
LONG idleCount = 0;
for (;;)
{
MSG msg;
if (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
theApp.PumpMessage();
else if (!theApp.OnIdle(idleCount++))
{
idleCount = 0;
if (!PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
{
HANDLE hdls[2] = { evQuit, hWhatever };
DWORD res = MsgWaitForMultipleObjects(2, hdls,
false, INFINITE, QS_ALLINPUT);
if (res == WAIT_OBJECT_0)
break;
else ...
}
}
}
Note carefully the second PeekMessage. It's necessary because an OnIdle handler could have reset the new input flag, causing MsgWaitForMultipleObjects to stall. If you were concerned about being flooded with messages or idle-time processing, such that there might be a significant delay before MsgWaitForMultipleObjects was called, you could "poll the handles" by injecting some WaitForMultipleObjects calls with timeout parameters of zero, and thus give the handles priority.
Q7. How can I control the user's interaction with my program in my MsgWaitForMultipleObjects loop?
A. You would need to replace this code from Q6 with a version that examines incoming messages:
if (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) theApp.PumpMessage();
For example, you might want to discard input and show the busy cursor while waiting, which can be appropriate if you need to process messages while waiting but disallow user input. However, the obvious approach of examining the message between PeekMessage and PumpMessage doesn't work. To understand why, consider that WM_PAINT is a low priority message that is deferred as long as higher priority messages are available to be processed. Thus, PeekMessage could return WM_PAINT, which is definitely a message we want to process, but PumpMessage makes its own call to GetMessage, which could retrieve a higher priority message, were one to be delivered between the PeekMessage and PumpMessage calls. This could be WM_KEYDOWN, for example, one of the messages we want to discard. My function PumpMessageDiscardingInputMessages avoids this message order inversion problem by ensuring we dispatch the exact message we peeked (and removed, another difference from the PeekMessage/PumpMessage sequence).
// For WM_KICKIDLE
#include <afxpriv.h>
// This function returns true if there was a message in the queue.
bool
PumpMessageDiscardingInputMessages(HWND hWnd)
{
// It's possible for PeekMessage to return WM_PAINT but for a subsequent
// GetMessage to return WM_KEYDOWN. Thus, we can't call PumpMessage
// directly, as it does its own GetMessage, which could cause us to
// process WM_KEYDOWN, one of the messages we want to discard.
MSG msg;
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message != WM_KICKIDLE)
{
if (IsInputMessage(msg)
&& (!hWnd
|| msg.hwnd == hWnd
|| (msg.hwnd && IsChild(hWnd, msg.hwnd))))
return true;
// This is one we want to handle, so emulate PumpMessage.
if (!AfxGetApp()->PreTranslateMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return true;
}
return false;
}
Now, we need to update our MsgWaitForMultipleObjects loop. Below, I'll use MsgWaitForMultipleObjectsEx, which as I mentioned in Q6, simplifies things somewhat, as it allows me to eliminate the new input issue:
LONG idleCount = 0;
for (;;)
{
if (PumpMessagesDiscardingInputMessages())
continue;
else if (!AfxGetApp()->OnIdle(idleCount++))
{
idleCount = 0;
HANDLE hdls[2] = { evQuit, hWhatever };
DWORD const res = MsgWaitForMultipleObjectsEx(
2,
hdls,
INFINITE,
QS_ALLINPUT,
MWMO_INPUTAVAILABLE);
if (res == WAIT_OBJECT_0)
break;
else ...
}
}
Two things are notable:
Finally, what's this function I used called IsInputMessage? It looks like this:
namespace {
template<size_t size>
inline bool
FindMsg(const UINT msg, const UINT (&array)[size])
{
return std::find(array, array+size, msg) != array+size;
}
}
bool
IsInputMessage(const UINT msg, const bool keys, const bool mice, const bool commands)
{
static const UINT keyMsgTab[] =
{
WM_KEYDOWN,
WM_KEYUP,
WM_SYSKEYDOWN,
WM_SYSKEYUP,
WM_CONTEXTMENU
};
static const UINT miceMsgTab[] =
{
WM_LBUTTONDOWN, WM_LBUTTONUP,
WM_LBUTTONDBLCLK,
WM_MBUTTONDOWN, WM_MBUTTONUP,
WM_MBUTTONDBLCLK,
WM_RBUTTONDOWN, WM_RBUTTONUP,
WM_RBUTTONDBLCLK,
WM_XBUTTONDOWN, WM_XBUTTONUP,
WM_XBUTTONDBLCLK,
WM_NCLBUTTONDOWN, WM_NCLBUTTONUP,
WM_NCLBUTTONDBLCLK,
WM_NCMBUTTONDOWN, WM_NCMBUTTONUP,
WM_NCMBUTTONDBLCLK,
WM_NCRBUTTONDOWN, WM_NCRBUTTONUP,
WM_NCRBUTTONDBLCLK,
WM_NCXBUTTONDOWN, WM_NCXBUTTONUP,
WM_NCXBUTTONDBLCLK,
};
static const UINT commandMsgTab[] =
{
WM_COMMAND,
WM_SYSCOMMAND,
WM_CONTEXTMENU
};
return (keys && FindMsg(msg, keyMsgTab))
|| (commands && FindMsg(msg, commandMsgTab))
|| (mice && FindMsg(msg, miceMsgTab));
}
This simple function identifies keyboard, mouse, and command messages, returning true if the message is one you're looking for, and false otherwise.
Q8. How can my thread notify another thread or a window that it has exited?
A. Sometimes it is useful for a thread to notify another thread that it exited. For example, the primary thread might want to be notified when a secondary thread has finished, so that it can free resources it allocated for use by the thread, change UI state to reflect the current "job" has finished, etc. Having the secondary thread notify the primary relieves the primary from polling the secondary to determine these things, and eliminating polling is a good thing. So how do we do it? The best way is to post a user-defined message in the WM_APP range or obtained from RegisterWindowMessage. (For more on user-defined messages, see Raymond Chen's article, Which message numbers belong to whom?) If the primary thread has created no windows but runs a single message loop to handle posted thread messages, then PostThreadMessage is a good choice. (To understand these restrictions and when that function can be a bad choice, see KB article PRB: PostThreadMessage Messages Lost When Posted to UI Thread.) Most of the time, however, the primary thread is an UI thread that manages windows, and PostThreadMessage is inappropriate for reasons given in the KB article. In such cases, the best approach is to designate a target window to receive the notification, which the secondary thread posts with PostMessage.
Of course, the secondary thread doesn't exit immediately after it PostMessages the UWM_THREADEXITED message, and for the reasons given in Q2, someone, usually the recipient of the exit message, must wait on the secondary thread to really exit. That is, your message handler should look something like this:
LRESULT ThreadOwnerWindow::OnThreadExited(WPARAM, LPARAM lp)
{
CWinThread* pThread = reinterpret_cast<CWinThread*>(lp);
threads[pThread] = 0; // 1
WaitForSingleObject(pThread->m_hThread);
delete pThread;
return 0;
}
The line commented (1) exists because the ThreadOwnerWindow owns the CWinThread object as discussed in Q1; after the thread has died, it's only appropriate for ThreadOwnerWindow to zero the pointer it holds to the thread. (You can perform this mapping however you like.) But what about the WaitForSingleObject call? Since it blocks the UI, isn't it bad? Not at all! Posting this message is the last thing the secondary thread does, and you can expect it to terminate without delay. Indeed, Q4 says about using WaitForSingleObject in a similar situation, "This works great if you've designed the secondary thread to exit quickly," and exit quickly it does if you've designed things properly.
Q9. SendMessage vs. PostMessage
To comment on this page, please send email to dsh@mvps.org.