我正在实现一个需要以下功能的线程:
- 及时响应终止请求
- 推送消息
- 在等待消息时保持对 SendMessage 请求的响应
我对消息泵的初始实现使用了 GetMessage
,如下所示:
while not Terminated and GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
我发现的问题是,除非有消息,否则 GetMessage 将永远不会返回。
这意味着,如果消息事件较少,可能需要相当长的一段时间才能再次检查已终止
。
我的第二个实现(受到 this answer 的启发)使用 MsgWaitForMultipleObjects
在检查之前等待消息存在(因为它有超时)
while not Terminated do
begin
if MsgWaitForMultipleObjects(0, nil^, False, 1000, QS_ALLEVENTS) = WAIT_OBJECT_0 then
begin
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
end;
我发现的问题是 MsgWaitForMultipleObjects
在等待时阻塞线程。因此,当通过 SendMessageTimeout
向线程发送消息时,它会超时,而使用 GetMessage
时不会超时。
想到的解决方案是返回到 GetMessage
实现,但添加一个计时器以确保 WM_TIMER
消息每秒重置循环。
这真的是唯一的方法吗? 看来应该有一些更好的方法来让线程在等待消息时保持响应。
最佳答案
My initial implementation of the message pump used GetMessage like:
while not Terminated and GetMessage(Msg, 0, 0, 0) do begin TranslateMessage(Msg); DispatchMessage(Msg); end;
The problem I found with that, is that GetMessage will never return unless there is a message. Meaning, if there is low message activity, it may be quite a while before it checks
Terminated
again.
您可以覆盖线程的虚拟 TerminatedSet()
方法通过 PostMessage()
或 PostThreadMessage()
向队列发布消息以“唤醒”GetMessage()
如果被阻止。
或者,让你的线程构造函数创建一个 TEvent
对象,并在线程的析构函数中释放它。然后让 TermatedSet()
发出该事件的信号。然后,您的循环可以使用 MsgWaitForMultipleObjects()
同时等待消息队列和事件。返回值将告诉您是否通过消息或事件满足了等待。
My second implementation (inspired by this answer) used
MsgWaitForMultipleObjects
to wait until a message exists before checking (since it has a timeout)while not Terminated do begin if MsgWaitForMultipleObjects(0, nil^, False, 1000, QS_ALLEVENTS) = WAIT_OBJECT_0 then begin while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin TranslateMessage(Msg); DispatchMessage(Msg); end; end; end;
The problem I've found with this, is that
MsgWaitForMultipleObjects
blocks the thread while it waits. So, when a message is sent to the thread viaSendMessageTimeout
, it times out, where it doesn't when usingGetMessage
.
SendMessage...()
系列函数将直接将消息传递到目标窗口的消息过程,完全绕过消息队列。因此 MsgWaitForMultipleObjects()
和 (Get|Peek)Message()
永远不会报告来自 SendMessage...() 的已发送消息
,仅来自 PostMessage()
或 PostThreadMessage()
的发布消息(或合成消息,如 WM_TIMER
、WM_PAINT
等)。但是,当跨线程边界发送消息时,接收线程仍然需要执行消息检索调用(即(Get|Peek)Message()
),以便发送消息实际传递给窗口过程。
The solution that comes to mind is to go back to the
GetMessage
implementation, but add a timer to make sure aWM_TIMER
message resets the loop every second.
在线程内,最好使用 waitable timer而不是 WM_TIMER
,那么您可以通过 MsgWaitForMultipleObjects()
使用计时器。但实际上,将 GetMessage()
与 WM_TIMER
一起使用与 MsgWaitForMultipleObjects()
与超时一起使用之间几乎没有什么区别,因此没有必要创建计时器浪费系统资源。
关于multithreading - 如何保持线程的消息泵 react 性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59076128/