原文地址:http://visualcpp.net/index.php?qID=49
How to wait for a thread termination?
Assume from main thread we created another thread. It's suppoused to do some work and finish. Sometimes main thread wants to know when the fired thread exits. Usually they recommend to use something like this:
HANDLE hThread; /* Creating thread, thread does some work... It may exit itself or we send the message to the thread to exit. */ switch(WaitForSingleObject(hThread, nTimeOut_IfAny)) { case WAIT_OBJECT_0 : // Thread exited, react somehow: fThreadFinished = true; break; case WAIT_TIMEOUT : // Timeout elapsed, thread still running fThreadFinished = false; break; default : // something wrong... };
Note that this waiting is performed in main thread, there could be some messages appear and from child thread too. But we don't pump these messages - that's not good indeed. This causes application been frozen (no keyboard response, no GUI update). If timeout is too long and child thread uses SendMessage()
function to send messages to main thread - deadlock happens. To avoid this situation we need to be waiting for the thread and still process message queue. Once I solved the problem this way:
while (true) { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) DispatchMessage(&msg); DWORD result = MsgWaitForMultipleObjects(1, &hThread, FALSE, nTimeOut, QS_ALLINPUT); if (result != (WAIT_OBJECT_0 + 1)) break; // Thread is exited }
This helped. Later I was advised to look how ATL function AtlWaitWithMessageLoop()
is implemented. I found my method is rather close to the truth. Here is a snippet of that function (from ATLBASE.h):
BOOL AtlWaitWithMessageLoop(HANDLE hEvent) { DWORD dwRet; MSG msg; while(1) { dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT); if (dwRet == WAIT_OBJECT_0) return TRUE; // The event was signaled if (dwRet != WAIT_OBJECT_0 + 1) break; // Something else happened // There is one or more window message available. Dispatch them while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) return TRUE; // Event is now signaled. } } return FALSE; }
where input hEvent
is the thread handle. This function won't return until thread is alive but message queue of calling thread (usually main one) will be pumped.
BOOL waitWithMessageLoop(HANDLE hEvent = 0, DWORD dwTimeout = INFINITE); BOOL waitWithMessageLoop(HANDLE hEvent, DWORD dwTimeout) { DWORD dwRet; MSG msg; hEvent = hEvent ? hEvent : CreateEvent(NULL, FALSE, FALSE, NULL); while(true) { dwRet = MsgWaitForMultipleObjects(1, &hEvent, FALSE, dwTimeout, QS_ALLINPUT); if (dwRet == WAIT_OBJECT_0) return TRUE; if (dwRet != WAIT_OBJECT_0 + 1) break; while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); if (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) return TRUE; } } return FALSE; }
There is another way to know if the thread has finished: before exit the child thread has to send a message to main thread. Hence the main thread should not wait the child in such a stupid manner, it only needs to handle that exit message. And there is no danger of deadlock.