退出时,工作线程sendmessage与主线程死锁

原文地址: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.

As a side effect I modified the function to use it instead of dummy API Sleep() function. You may wait for thread now with or without timeout or just wait for certain time without locking of message queue. Calls:
waitWithMessageLoop(0, 2000)
waitWithMessageLoop(hThread)
waitWithMessageLoop(hThread, 5000)

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值