在 调用SendMessage 产生死锁的问题分析 之后,我在界面程序中不再使用 WaitForSingleObject 了,而改用如下的函数
DWORD WaitObjectAndMsg(HANDLE hEventThread, DWORD dwMilliseconds)
{
BOOL bWait = TRUE;
DWORD dwEvt = 0;
while(bWait)
{
DWORD dwEvt = ::MsgWaitForMultipleObjects(1 , &hEventThread, FALSE, dwMilliseconds, QS_ALLINPUT);
switch (dwEvt)
{
case WAIT_OBJECT_0:
{
return WAIT_OBJECT_0;
}
break;
case WAIT_OBJECT_0 + 1:
{
MSG msg;
while ( ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
{
if( WM_CLOSE == msg.message || WM_QUIT == msg.message )
{
return WAIT_FAILED;
break;
}
else
{
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
break;
}
default:
{
return dwEvt;
break;
}
}
}
return dwEvt;
}
这个函数看起来不错,在等待一个 Object 的同时,还可以边等待,边处理消息
今天才发现,这样处理有缺陷
如果在等待一个Object 时,哪怕只有 0.00001 秒,这里如果一个不小心处理了消息
那么 很自然会走到这个语句 ::DispatchMessage(&msg);
经过调试发现, ::DispatchMessage(&msg); 是阻塞的,
如果在处理消息的函数里,调用了一个
CDialog dlg;
dlg.DoModal(); 这里也是阻塞的, 如果这个窗口一直不关闭,
即使这时候 Object 已经响应了,但这个函数仍然不返回,,,,,,,因为它在双重的消息处理中。。。。。。。
下面是可参考解决办法
One danger of theMsgWaitForMultipleObjects functionis calling it when there are already messages waiting to be processed, because
MsgWaitForMultipleObjectsreturns only when there is a
new event in the queue.
In other words, consider the following scenario:
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)returnsTRUEindicating that there is a message.- Instead of processing the message, you ignore it and call
MsgWaitForMultipleObjects.
This wait will not return immediately, even though there is a message in the queue. That's because the call toPeekMessage told you that a message was ready, and you willfully ignored it. TheMsgWaitForMultipleObjects message tells you only when there are new messages; any message that you already knew about doesn't count.
A common variation on this is the following:
MsgWaitForMultipleObjectsreturns that there is a message.- You call
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)and process that message. - You call
MsgWaitForMultipleObjectsto wait for more messages.
If it so happens that there were two messages in your queue, theMsgWaitForMultipleObjects does not return immediately, because there are no new messages; there is an old message you willfully ignored, however.
When MsgWaitForMultipleObjects tells you that there is a message in your message queue, you have to processallof the messages untilPeekMessage returnsFALSE, indicating that there are no more messages.
Note, however, that this sequence is not a problem:
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)returnsFALSEindicating that there is no message.- A message is posted into your queue.
- You call
MsgWaitForMultipleObjectsand include theQS_ALLPOSTMESSAGEflag.
This wait does return immediately, because the incoming posted message sets the "There is a new message in the queue that nobody knows about" flag, whichQS_ALLPOSTMESSAGE matches and therefore causesMsgWaitForMultipleObjects to return immediately.
TheMsgWaitForMultipleObjectsEx functionlets you pass theMWMO_INPUTAVAILABLE flag to indicate that it should check for previously-ignored input.
Armed with this knowledge, explain why the observed behavior with the following code is "Sometimes my program gets stuck and reports one fewer record than it should. I have to jiggle the mouse to get the value to update. After a while longer, it falls two behind, then three..."
// Assume that there is a worker thread that processes records and
// posts a WM_NEWRECORD message for each new record.
BOOL WaitForNRecords(HANDLE h, UINT cRecordsExpected)
{
MSG msg;
UINT cRecords = 0;
while (true) {
switch (MsgWaitForMultipleObjects(1, &h,
FALSE, INFINITE, QS_ALLINPUT)) {
case WAIT_OBJECT_0:
DoSomethingWith(h); // event has been signalled
break;
case WAIT_OBJECT_1:
// we have a message - peek and dispatch it
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (SendMessage(hwndNotify, WM_GETRECORDCOUNT,
0, 0) >= cRecordsExpected) {
return TRUE; // we got enough records
}
break;
default:
return FALSE; // unexpected failure
}
}
}

本文探讨了在界面程序中使用MsgWaitForMultipleObjects替代WaitForSingleObject进行消息与事件同步时可能遇到的问题,特别是双重消息处理导致的死锁现象,并提供了解决方案。
1496





