12

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

谢谢

4

2 回答 2

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;
于 2012-12-18T11:22:37.710 回答
3

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

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

除了 PeekMessage,您还可以使用GetMessage,等待收到消息。GetMessage 返回 0返回false*)

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

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

* )关于 GetMessage 的返回值:我注意到 Delphi 将 GetMessage 实现为返回一个 LongBool。然而,实际的返回值是一个整数。在 WM_QUIT 的情况下返回 0,在错误的情况下返回 -1,在其他情况下返回非零。微软声明

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

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

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

于 2012-12-18T11:20:52.953 回答