当我的主应用程序(Delphi2009)终止时,我想让它通知我的线程(定时器、带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。我做错了什么,我的线程似乎没有接收到消息?
发布于 2014-02-10 07:17:04
一个显而易见的解释是你的线程中没有消息泵。你发布了消息,但是线程并不抽取它的队列。
不过,代码是不必要的复杂。似乎根本不需要消息。调用线程的Terminate
方法,然后使用它的WaitFor
方法等待线程停止。或者更简单,只需在线程上调用Free
。
你的代码确实包含了一些奇怪的东西:
PeekMessage
?据我所知,那没有任何用处。应避免使用Sleep
等待bpvccMAILER
为nil
,然后再使用FreeAndNil(bpvccMAILER)
。GetLastError
。通常情况下,这仅在前面的API调用失败时才会发生。并且失败由API调用返回的值指示。发布于 2017-05-10 17:04:38
只需调用PostThreadMessage并返回,之后不会有任何睡眠循环。
如果您需要等到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;
https://stackoverflow.com/questions/21670162
复制