我的应用程序有几个主题: 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;
答案 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期间发送的消息不会丢失。
奇怪的是,目前我找不到参考资料,但我的猜测是新闻组社区。 p>
答案 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;