在 调用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
MsgWaitForMultipleObjects
returns only when there is a
new event in the queue.
In other words, consider the following scenario:
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)
returnsTRUE
indicating 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:
MsgWaitForMultipleObjects
returns that there is a message.- You call
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)
and process that message. - You call
MsgWaitForMultipleObjects
to 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)
returnsFALSE
indicating that there is no message.- A message is posted into your queue.
- You call
MsgWaitForMultipleObjects
and include theQS_ALLPOSTMESSAGE
flag.
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
}
}
}