Delphi多线程消息循环

时间:2008-10-23 13:05:21

标签: multithreading delphi winapi

我的应用程序有几个主题: 1)主线程 2)TFQM使用的2个子主线程(每个都有消息循环,如下所示) 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;

3 个答案:

答案 0 :(得分:11)

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

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

2)AllocateHwnd在试试看之后!如果失败,则不应调用DeallocateHwnd。

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

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

如果我可以在此处插入完全免费的产品 - 请使用我的OmniThreadLibrary代替。比直接使用Windows消息传递更简单。

答案 1 :(得分:9)

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

我认为您正在创建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消息开始的原因是确保在初始化threadprocedure期间发送的消息不会丢失。

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

答案 2 :(得分:7)

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;