multithreading - 如何保持线程的消息泵 react 性

标签 multithreading delphi winapi

我正在实现一个需要以下功能的线程:

  1. 及时响应终止请求
  2. 推送消息
  3. 在等待消息时保持对 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 via SendMessageTimeout, it times out, where it doesn't when using GetMessage.

SendMessage...() 系列函数将直接将消息传递到目标窗口的消息过程,完全绕过消息队列。因此 MsgWaitForMultipleObjects()(Get|Peek)Message() 永远不会报告来自 SendMessage...() 的已发送消息,仅来自 PostMessage()PostThreadMessage()发布消息(或合成消息,如 WM_TIMERWM_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 a WM_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/

相关文章:

c# - keydown事件取消其他c#

c# - 后台 worker 异常处理

winapi - 捕获 Win32 消息

c++ - 如何解决窗口焦点问题?

c - send() 和 receive() 使用一个套接字?

multithreading - Redis 服务器在多线程 Perl 脚本中响应 'Resource temporarily unavailable'

multithreading - 终止线程,退出前运行代码

delphi - 自定义图形的指针操作无效+运行时错误

delphi - 在Delphi中创建一个选项表格

delphi - 文件存在和修改日期的问题