【问题标题】:Delphi Multi-Threading Message LoopDelphi 多线程消息循环
【发布时间】:2010-09-18 18:45:21
【问题描述】:

我的应用程序有几个线程: 1) 主线程 2)2个子主线程(每个都有Message Loop,如下图),TFQM使用 3)n个工作线程(简单循环,包含Sleep())

我的问题是,当我关闭我的应用程序时,工作线程设法正确退出,但是当我发出 WM_QUIT 关闭它们时,2 个子主线程中的 1 个挂起(从不退出)。


procedure ThreadProcFQM(P: Integer); stdcall;
var
  Msg: TMsg;
 _FQM: TFQM;
begin
  _FQM := Ptr(P);
  try
    _FQM.fHandle := AllocateHwnd(_FQM.WndProc);

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

  finally
    DeallocateHWnd(_FQM.fHandle);
    SetEvent(_FQM.hTerminated);
  end;
end;

procedure TFQM.Stop;
begin
  PostMessage(fHandle, WM_QUIT, 0, 0);

  WaitForSingleObject(hTerminated, INFINITE);
  if hThread <> INVALID_HANDLE_VALUE then
  begin
    CloseHandle(hThread);
    hThread := INVALID_HANDLE_VALUE;
  end;
end;

【问题讨论】:

    标签: multithreading delphi winapi


    【解决方案1】:

    我遇到了同样的问题,我发现我不应该创建一个隐藏窗口来接收消息。线程已经有一个消息系统。

    我认为您正在创建 Windows 句柄并将其存储在 fHandle 中,但 GetMessage 会检查您线程的消息循环。因此消息 PostMessage(fHandle, WM_QUIT, 0, 0); getmesssage 永远不会收到。

    您可以使用 PostThreadMessage 将消息发布到您的线程,并在线程中使用 GetMessage(CurrentMessage, 0, 0, 0)。唯一重要的区别是您必须通过调用

    从您的线程启动消息循环
    PeekMessage(CurrentMessage, 0, WM_USER, WM_USER, PM_NOREMOVE);
    

    你应该从这个开始,而不是你的设置,然后开始你的循环。

    您应该从 peek 消息开始的原因是确保在线程过程初始化期间发送的消息不会丢失。

    奇怪的是,目前我找不到参考资料,但我猜是新闻组社区。​​p>

    【讨论】:

    • 哎呀,对不起我第一次来这里。不知道cmets....我会试一试。谢谢。
    • Davy 是对的:应该使用 PeekMessage 来创建消息队列
    • 您在MSDN page for PostThreadMessage 上阅读它。他们甚至建议让主线程在创建消息后等待线程设置的事件。
    【解决方案2】:

    1) 您不需要在线程中使用 AllocateHwnd。首次调用 GetMessage 将为该线程创建一个单独的消息队列。但是为了向线程发送消息,您应该使用 PostThreadMessage 函数。

    请注意,在调用 PostThreadMessage 的那一刻,仍然无法创建队列。我通常使用构造:

    while not PostThreadMessage(ThreadID, idStartMessage, 0, 0) do
      Sleep(1);
    

    确保消息队列已创建。

    2) 为了终止线程循环,我定义了自己的消息:

      idExitMessage = WM_USER + 777; // you are free to use your own constant here
    

    3) 不需要单独的事件,因为您可以将线程句柄传递给 WaitForSingleObject 函数。因此,您的代码可能如下所示:

      PostThreadMessage(ThreadID, idExitMessage, 0, 0);
      WaitForSingleObject(ThreadHandle, INFINITE);
    

    考虑到 ThreadID 和 ThreadHandle 是不同的值。

    4) 因此,您的 ThreadProc 将如下所示:

    procedure ThreadProcFQM; stdcall;
    var
      Msg: TMsg;
    begin
      while GetMessage(Msg, 0, 0, 0) 
        and (Msg.Message <> idExitMessage) do
      begin
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      end;
    end;
    

    【讨论】:

    • 如下所述,您应该使用 PeekMessage 创建消息队列: // 强制创建消息队列 PeekMessage(Msg, 0, WM_USER, WM_USER, PM_NOREMOVE);
    【解决方案3】:

    如果我可以指出您的代码中的一些问题...

    1) 您没有检查 AllocateHwnd 的输出。是的,很可能它永远不会失败,但仍然......

    2) AllocateHwnd 退出 try..finally!如果失败,则不应调用 DeallocateHwnd。

    3) AllocateHwnd 不是线程安全的。如果同时从多个线程调用它,可能会遇到问题。 Read more.

    正如戴维所说,使用 MsgWaitForMultipleObjects 而不是创建隐藏的消息窗口。然后使用 PostThreadMessage 向线程发送消息。

    如果我可以在这里插入一个完全免费的产品的插件 - 请改用我的 OmniThreadLibrary。比直接使用 Windows 消息传递要简单得多。

    【讨论】:

    • D2007+,实际上。是时候升级了,然后:)
    • Gabr,您的线程库看起来很棒 - 感谢您指出。哦,我会说你的网站是“斯巴达式的”,而不是丑陋的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    • 2013-01-07
    • 2012-05-14
    • 2012-05-24
    • 1970-01-01
    • 2012-02-07
    • 1970-01-01
    相关资源
    最近更新 更多