Delphi:PostThreadMessage& PeekMessage无法正常工作

时间:2014-02-10 06:07:39

标签: multithreading delphi delphi-2009

当我的主应用程序(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。我做错了什么,似乎我的线程没有收到消息?

2 个答案:

答案 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;