Thread中的MessageLoop

时间:2012-12-18 11:17:19

标签: delphi delphi-xe2

如何使用OTL在线程中实现消息循环? Application.ProcessMessages;是我到目前为止使用的,但使用起来并不安全。

由于

2 个答案:

答案 0 :(得分:17)

这是我从线程队列中拉出消息的方式:

while GetMessage(Msg, 0, 0, 0) and not Terminated do begin
  Try
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  Except
    Application.HandleException(Self);
  End;
end;

使用Application.ProcessMessages将拉出调用线程队列的消息。但是在消息循环中使用它是不合适的,因为它不会阻塞。这就是你使用GetMessage的原因。如果队列为空,它会阻塞。并且Application.ProcessMessages还调用其他TApplication方法,这些方法并非设计为线程安全的。所以有很多理由不从主线程以外的线程调用它。

当你需要终止线程时,我这样做:

Terminate;
PostThreadMessage(ThreadID, WM_NULL, 0, 0);
//wake the thread so that it can notice that it has terminated

这些都不是特定于OTL的。此代码全部用于TThread后代。但是,这些想法是可以转移的。


在注释中,表明您要运行繁忙的非阻塞消息循环。您可以使用PeekMessage

while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
  Try
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  Except
    Application.HandleException(Self);
  End;
end;

答案 1 :(得分:3)

您可以构建一个类似于Application.Run中的循环。

您可以拨打PeekMessage,检查邮件是否可用。 PeekMessage检查当前线程的消息队列,因此如果您在线程中使用它,它会检查线程的消息队列(您可以使用PostThreadMessage向其发布消息)。

您也可以使用GetMessage代替PeekMessage,它会一直等到收到消息。 GetMessage 返回0 返回false *)

当它收到WM_QUIT消息时,这也是终止消息循环的信号。之后再次调用GetMessage是有风险的,因为它可能不会收到另一条消息,并且可能会阻止您的应用程序正常关闭,因为它正在阻塞。

Application.ProcessMessages确实不是很安全,因为它会执行许多特定于主线程的附加功能。例如,一旦消息队列为空,它就会触发Application.OnIdle,这意味着它在线程消息队列为空时调用(问题1)并且在线程的上下文中调用(问题2),不允许任何事件中的VCL互动。

* 关于GetMessage的返回值:我注意到Delphi将GetMessage实现为返回LongBool。但实际返回值是整数。在WM_QUIT的情况下返回0,在出现错误时返回-1,在其他情况下返回非零。 Microsoft states

  

因为返回值可以是非零,零或-1,所以避免使用类似的代码   这样:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...

不幸的是,您必须在不同的别名下导入GetMessage才能正确使用它。