当我的主应用程序(Delphi 2009)终止时,我希望它能够向我的线程(定时器,带有ADO连接的TDataModules,SMTP等)发出正确处理信号。
在我的主要申请中,我有以下内容:
try
PostThreadMessage(bpvccMAILER.ThreadID, WM_SYSTEM_CLOSE, self.Handle, 0);
returnMessage := (SysErrorMessage(GetLastError)); //Returns 'The operation completed successfully'
while TRUE do
begin
sleep(1000);
if not (Assigned(bpvccMAILER)) then
begin
bpvccACTIVITY_LOGGER.Write('SHUTDOWN','TBPVCommunicatorGUI.FormClose - All Threads have shut down');
break;
end;
locWaited := locWaited + 10;
end;
except
end;
finally
FreeAndNil(bpvccACTIVITY_LOGGER);
FreeAndNil(bpvccMAILER);
end;
线程类:
TBPVMailer = class(TThread)
protected
SMTP : TIdSMTP;
interval : Integer;
fMain : Integer;
fMainIsSvc : Boolean;
fTerminated: Boolean;
function SendEmail(AEmail: TEmailObj) : TBPVEmailSendResult;
function doSleep : Boolean;
procedure Write(AStatus, AMessage : String);
procedure FlushQueue();
procedure HandleMessage(var Message : TMessage); message WM_SYSTEM_CLOSE;
public
constructor Create(AServer : String; APort : Integer; AUser, APass : String; AInterval : Integer; StartSuspended : Boolean); overload;
procedure Execute; override;
procedure QueueEmail(AEmail: TEmailObj; EmailType : TBPVEmailType; AssociatedID : String);
destructor Destroy; override;
end;
procedure TBPVMailer.HandleMessage(var Message: TMessage);
var
msg : tagMSG;
begin
PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
fMain := Message.WParam;
fMainIsSvc := Message.LParam = 1;
fTerminated := TRUE;
end;
问题是,Assigned(bpvccMAILER)总是在调用PostThreadMessage后返回true。此外,bpvccMAILER.fTerminated始终为FALSE,这意味着从未执行TBPVMailer.HandleMessage,因为该值设置为TRUE。我做错了什么,似乎我的线程没有收到消息?
答案 0 :(得分:4)
明显的解释是你的线程中没有消息泵。您发布消息,但线程不会抽取其队列。
但代码不必要复杂。似乎根本不需要消息。调用线程的Terminate
方法,然后使用其WaitFor
方法等待它停止。或者甚至更简单,只需在线程上调用Free
即可。
您的代码确实包含许多奇怪的内容:
PeekMessage
?这没有任何我无法辨别的目的。 Sleep
等待。您几乎总能使用专用等待功能。 bpvccMAILER
nil
,然后使用FreeAndNil(bpvccMAILER)
,这很奇怪。 GetLastError
。通常,只有在先前的API调用失败时才会这样。并且API调用返回的值表示失败。 答案 1 :(得分:0)
主线程中的Sleep循环不正常,因为它阻止处理消息。
只需调用PostThreadMessage并返回,之后没有任何Sleep循环。
如果您需要等到bpvccMAILER完成,请添加完成后发送PostMessage到主线程的代码。因此,主线程将处理此消息并将意识到辅助线程已完成。从一开始就以这种方式改变你的应用程序可能并不容易,但是你将一点一点地设计应用程序,以便始终能够正确处理线程。
除此之外,如果你使用PostThreadMessage,那么你的Thread.Execute循环必须有MsgWaitForMultipleObjects。
以下是Thread.Execute循环必须如何的示例:
<skipped>
repeat
<skipped>
R := MsgWaitForMultipleObjects(EventCount, EventArray, False, INFINITE, QS_ALLINPUT);
<skipped>
if R = WAIT_OBJECT_0 + EventCount then
begin
while PeekMessage(M, 0, 0, 0, PM_REMOVE) do
begin
if M.Message = WM_QUIT then
Break;
TranslateMessage(M);
DispatchMessage(M);
end;
if M.Message = WM_QUIT then
Break;
end;
<skipped>
until Terminated;
<skipped>
如果你的应用程序最终需要在胎面运行时退出(假设你的线程对象是T变量),请执行以下操作:
T.Terminate;
SetEvent([one of the event of the EventArray]); // we should call it AFTER terminate for the Terminated property would already be True when the tread exits from MsgWaitForMultipleObjects
T.WaitFor;
T.Free; // "Free" calls "WaitFor" anyway, but Remy Lebeau suggests to explicitly call "WaitFor" before "Free".
T := nil;