1

我的 Delphi 应用程序中有一个线程,它有一个消息等待循环。每次收到消息时,它都会开始做一些工作。这是该线程的执行过程:

procedure TMyThread.Execute;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do
  begin
    {thread message}
    if Msg.hwnd = 0 then
    begin
      ...
    end
    else
      DispatchMessage(Msg);
  end;
end;

使用我的应用程序进行一些测试时,我发现 GetMessage 函数依赖于主线程。我的意思是,当主线程正在做一些工作时,我线程中的 GetMessage 函数不会返回,即使一条消息正在等待它接收(消息是由另一个线程使用 PostThreadMessage 函数发送的: PostMessage( MyThreadId, WM_MyMessage, 0, 0))。

只有当主线程完成其工作或调用 Application.ProcessMessages 方法时,GetMessage 才会返回并且我的线程开始工作。实现这种线程间通信我确信我的线程会独立工作,而且我永远不会期望直接发送到线程的消息的接收将依赖于主线程。

在进行测试时,我在主线程中使用了 WaitForSingleObject 函数,等待事件几秒钟。这是当我注意到我的线程没有做任何工作时,即使消息是由另一个线程发送给它的。当 WaitForSingleObject 函数终于完成等待并且主线程变得空闲时,我线程中的 GetMessage 函数返回了。

有人可以解释一下为什么它会这样工作吗?有解决方法吗?我想让我的线程独立接收消息。我所有的线程都是由主线程创建的。这可能是原因吗?

提前感谢您的帮助。

马吕斯。


Mghie,你又完全正确(你最近帮我处理了消息传递的问题,你可能还记得)。正如您所建议的, GetMessage 立即返回,但实际上,线程在调用主窗口方法时挂起:

procedure TMyThread.Execute;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do
  begin
    {thread message}
    if Msg.hwnd = 0 then
    begin
      ...
      if Assigned(FOnCommEventMethod) then
        FOnCommEventMethod(FCommEventsQueueItem);
      ...
    end
    else
      DispatchMessage(Msg);
  end;
end;

FOnCommEventMethod 是一个对象的方法,声明为'procedure (EventMask: Cardinal) of object;' (此线程处理串行端口事件)。在这种情况下,FOnCommEventMethod 被分配了一个属于主窗体类的过程。当我的线程调用该方法时,线程挂起等待主线程完成其工作。

怎么会?如您所见,我不使用 Synchronize() 方法来调用此过程。因此,我不希望我的线程与主线程同步。它是否隐含地发生?顺便说一句,我知道除主线程之外的任何其他线程都不应访问任何 GUI 组件,因此应使用 Synchronize 方法,但我现在只做一些快速测试。

回到 WaitForSingleObject 主题,我知道我不应该使用它,但这只是一个测试(巧合地)我注意到了这个问题。

谢谢你的帮助。如果您不帮助我,我可能会摆脱消息传递并改用事件,最后我会注意到这不是原因:-)。

4

3 回答 3

3

只有当主线程完成其工作或调用 Application.ProcessMessages 方法时,GetMessage 才会返回并且我的线程开始工作。

我怀疑这是否真的发生了。AFAIK 这两个消息循环应该彼此独立,只要您不使用其他同步线程的方法,例如SendMessage()。您确定线程确实阻塞在GetMessage()内部而不是Synchronize( ) 内部(在内部使用SendMessage())?

在进行测试时,我在主线程中使用了 WaitForSingleObject 函数,等待事件几秒钟。

你永远不应该在主线程中使用WaitForSingleObject()超时时间超过 100 毫秒,因为这会使你的 GUI 显得迟缓。事实上,我建议你不要在主线程中使用它,因为这只是轮询。而是从您的工作线程发布消息。

于 2009-03-08T10:33:09.323 回答
2

您可以为您的线程创建一个消息队列。只需在您的线程中执行以下代码:

MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
SetEvent(messageQueueReady);
while (GetMessage(&msg, NULL, 0, 0)) 
{
    ...// do message processing here
}

对 PeekMessage 的调用会强制操作系统为您的线程创建一个新的消息队列。您必须确保同步这一点,即您必须等待(例如通过WaitForSingleObject)调用成功,然后才能将消息发布到该线程(例如通过PostThreadMessage)。这就是上面示例中调用 SetEvent API 的原因。

编辑:对不起,示例在 C 中,希望这对你没问题。

于 2009-03-08T01:31:14.540 回答
0

请参阅http://groups.google.com/group/borland.public.delphi.language.delphi.win32/browse_thread/thread/cf368834a606321b

要求蒸馏,留下许多魔鬼隐藏的粗糙细节:主(或 VCL)线程在 Delphi 中是特殊的,因为引擎盖下的许多东西不是线程安全的,它拥有它们全部。消息循环之类的事情最终会因为与引擎盖下的 VCL 线程同步的事情而等待。很多人对此都有理论。似乎效果最好的事情是让您的 VCL 线程尽可能轻/响应,以尽量减少等待。每个人都非常同意这一点。有些人对其他可能有用的事情有想法,但其他人认为他们只是在自找麻烦。

于 2009-03-07T23:07:56.210 回答