如何使线程的消息泵保持被动状态

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

  1. 迅速响应终止请求
  2. 抽取消息
  3. 在等待消息时保持对SendMessage请求的响应

我最初对消息泵的实现使用了GetMessage,例如:

while not Terminated and GetMessage(Msg,0) do
begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;

我发现的问题是,除非有消息,否则GetMessage将永远不会返回。 这意味着,如果消息活动较少,则可能需要一段时间才能再次检查Terminated

我的第二个实现(受this answer启发)使用MsgWaitForMultipleObjects等到消息存在后再检查(因为它有超时)

while not Terminated do
begin
  if MsgWaitForMultipleObjects(0,nil^,False,1000,QS_ALLEVENTS) = WAIT_OBJECT_0 then
  begin
    while PeekMessage(Msg,PM_REMOVE) do
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
  end;
end;

我发现的问题是MsgWaitForMultipleObjects在等待线程时阻塞了线程。因此,当一条消息通过SendMessageTimeout发送到线程时,它超时了,而在使用GetMessage时却没有。

想到的解决方案是返回到GetMessage实现,但是添加一个计时器以确保WM_TIMER消息每秒重置一次循环。

这真的是唯一的方法吗? 似乎应该有一些更好的方法来使线程在等待消息时保持响应。

nidi_1 回答:如何使线程的消息泵保持被动状态

  

我对消息泵的初始实现使用GetMessage,如:

while not Terminated and GetMessage(Msg,0) do
begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;
     

我发现的问题是,除非有消息,否则GetMessage将永远不会返回。这意味着,如果消息活动较少,则可能需要一段时间才能再次检查Terminated

您可以重写线程的虚拟TerminatedSet()方法以通过PostMessage()PostThreadMessage()将消息发布到队列中以“唤醒” {{1} }(如果被阻止)。

或者,让您的线程构造函数创建一个TEvent对象,并将其释放到线程的析构函数中。然后让GetMessage()发出该事件信号。然后,循环可以使用TerminatedSet()同时等待消息队列和事件。返回值将告诉您等待是通过消息还是事件完成的。

  

我的第二个实现(受this answer启发)使用MsgWaitForMultipleObjects()等到消息存在后再检查(因为它有超时)

MsgWaitForMultipleObjects
     

我发现的问题是while not Terminated do begin if MsgWaitForMultipleObjects(0,nil^,False,1000,QS_ALLEVENTS) = WAIT_OBJECT_0 then begin while PeekMessage(Msg,PM_REMOVE) do begin TranslateMessage(Msg); DispatchMessage(Msg); end; end; end; 在等待线程时阻塞了线程。因此,当一条消息通过MsgWaitForMultipleObjects发送到线程时,它超时了,而在使用SendMessageTimeout时却没有。

GetMessage函数家族将消息直接传递到目标窗口的消息过程,而完全绕过消息队列。因此,SendMessage...()MsgWaitForMultipleObjects()将永远不会报告来自(Get|Peek)Message()已发送消息,只会报告来自SendMessage...()已发布消息,或者PostMessage()(或综合消息,例如PostThreadMessage()WM_TIMER等)。但是,当发送消息跨越线程边界时,接收线程仍需要执行消息检索调用(WM_PAINT),以便将 send 消息发送给实际传递到窗口过程。

  

想到的解决方案是返回到(Get|Peek)Message()实现,但是添加一个计时器以确保GetMessage消息每秒重置一次循环。

在线程内,最好使用waitable timer而不是WM_TIMER,然后可以将计时器与WM_TIMER一起使用。但是,实际上,MsgWaitForMultipleObjects()GetMessage()一起使用WM_TIMERMsgWaitForMultipleObjects()的超时几乎没有什么区别,因此无需浪费系统资源来创建计时器。

本文链接:https://www.f2er.com/3019360.html

大家都在问