Delphi 等待TThread实例启动的正确方法是什么

Delphi 等待TThread实例启动的正确方法是什么,delphi,tthread,Delphi,Tthread,在TThread实例创建和启动之间,主线程将继续执行代码。若主线程中的代码依赖于要完全启动和运行的线程,那个么它必须以某种方式等待线程Execute方法实际启动 考虑以下代码: const WM_MY_ACTION = WM_APP + 10; type TWndThread = class(TThread) protected fWndHandle: THandle; IsRunning: boolean; procedure WndProc(var Ms

TThread
实例创建和启动之间,主线程将继续执行代码。若主线程中的代码依赖于要完全启动和运行的线程,那个么它必须以某种方式等待线程
Execute
方法实际启动

考虑以下代码:

const
  WM_MY_ACTION = WM_APP + 10;

type
  TWndThread = class(TThread)
  protected
    fWndHandle: THandle;
    IsRunning: boolean;
    procedure WndProc(var Msg: TMessage);
    procedure Execute; override;
  public
    Test: integer;
    procedure AfterConstruction; override;
    procedure DoAction;
  end;

procedure TWndThread.AfterConstruction;
begin
  inherited;
  while not IsRunning do Sleep(100); // wait for thread start up
end;

procedure TWndThread.Execute;
var
  Msg: TMsg;
begin
  fWndHandle := AllocateHWnd(WndProc);
  IsRunning := true;
  try
    while not Terminated do
      begin
        if MsgWaitForMultipleObjects(0, nil^, False, 1000, QS_ALLINPUT) = WAIT_OBJECT_0 then
          begin
            while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
              begin
                TranslateMessage(Msg);
                DispatchMessage(Msg);
              end;
          end;
      end;
  finally
    DeallocateHWnd(fWndHandle);
  end;
end;

procedure TWndThread.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_MY_ACTION:
      begin
        inc(Test);
      end;
    else Msg.Result := DefWindowProc(fWndHandle, Msg.Msg, Msg.WParam, Msg.LParam);
  end;
end;

procedure TWndThread.DoAction;
begin
  PostMessage(fWndHandle, WM_MY_ACTION, 0, 0);
end;

var
  t: TWndThread;
begin
  t := TWndThread.Create;
  t.DoAction;
  t.Terminate;
end;
如果没有等待
IsRunning
标志的循环,
DoAction
将无法成功地将消息发布到包含的窗口句柄,因为它尚未创建。基本上,
inc(Test)
内部
WndProc
不会被触发

Execute
方法中,是否有更好的方法来等待线程启动并完成必要的初始化,或者这个解决方案是否达到了预期效果


注意:我知道
AllocateHWnd
DeallocateHWnd
不是线程安全的,不应该像上面的示例那样在生产代码中使用。

主线程

  • 创建一个事件。例如,
    TSimpleEvent
    将满足您的需要
  • 将事件设置为无信号。对于
    TSimpleEvent
    ,这是对
    ResetEvent
    的调用。我希望一个新制作的
    t简单事件
    会处于无信号状态,但在我脑海里我记不起那个细节
  • 创建线程,在构造函数中传递事件
  • 等待事件发出信号。对于
    TSimpleEvent
    ,这意味着调用
    WaitFor
  • 工作线程

  • 记下传递给线程构造函数的事件
  • 在线程执行开始时,向事件发送信号。对于
    TSimpleEvent
    ,这意味着调用
    SetEvent

  • 正如David所指出的,一个
    TEvent
    可以实现这一点,其他任何数量的同步对象也可以。举个例子(反正我已经写完了):


    FIsRunning
    Boolean
    更改为
    TEvent
    ,以便在一切就绪时收到信号

    现在,您可以随时等待此事件(尤其是在公共方法中,如
    DoAction
    ):

    我想我明白你的意思了:

    uses
      Windows, SysUtils, Classes;
    
    type
      TMyThread = class(TThread)
      private
        FStartEvent: THandle;
      protected
        procedure Execute; override;
      public
        procedure AfterConstruction; override;
      end;
    
    implementation
    
    { TMyThread }
    
    procedure TMyThread.AfterConstruction;
    begin
      FStartEvent:= CreateEvent(nil, True, False, nil);
      inherited;  // this starts the thread
      if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
     // means the thread finished;
     // should not happen but you can check it to be safe
        then ..;
     // otherwise nothing should be done
    end;
    
    procedure TMyThread.Execute;
    begin
      SetEvent(FStartEvent);
    // ...
      CloseHandle(FStartEvent);
    end;
    
    或者您可以按照鲁福爵士的建议,将
    WaitForSingleObject
    AfterConstruction
    移动到您的
    DoAction
    代码:

    procedure TMyThread.CheckRunning;
    begin
      if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
      then // the thread already finished;
           // this should not normally happen,
           // maybe application is terminating or something else unexpected.
    //   ..;
    // else the thread is up and running here. 
    end;
    

    我编辑了问题-将等待代码移动到
    AfterConstruction
    ,以使线程类更加独立。我想没有什么能阻止我以一种内敛的方式使用
    TSimpleEvent
    ,也是吗?等待的最佳场所是
    DoAction
    方法或任何其他公共方法,在这些方法中,您必须确保一切正常running@SirRufo是正确的,只有在需要等待的时候才等待是更好的,只要想想是否每个线程都需要2秒钟才能开始运行。仅创建10个线程将阻塞20秒。看看我的答案,没有人会等到行动的时候
    var
      t: TWndThread;
    begin
      t := TWndThread.Create;
      try
        t.DoAction;
      finally
        t.Free;
      end;
    end;
    
    uses
      Windows, SysUtils, Classes;
    
    type
      TMyThread = class(TThread)
      private
        FStartEvent: THandle;
      protected
        procedure Execute; override;
      public
        procedure AfterConstruction; override;
      end;
    
    implementation
    
    { TMyThread }
    
    procedure TMyThread.AfterConstruction;
    begin
      FStartEvent:= CreateEvent(nil, True, False, nil);
      inherited;  // this starts the thread
      if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
     // means the thread finished;
     // should not happen but you can check it to be safe
        then ..;
     // otherwise nothing should be done
    end;
    
    procedure TMyThread.Execute;
    begin
      SetEvent(FStartEvent);
    // ...
      CloseHandle(FStartEvent);
    end;
    
    procedure TMyThread.CheckRunning;
    begin
      if WaitForSingleObject(FStartEvent, INFINITE) = WAIT_FAILED
      then // the thread already finished;
           // this should not normally happen,
           // maybe application is terminating or something else unexpected.
    //   ..;
    // else the thread is up and running here. 
    end;