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

Dal*_*kar 6 delphi tthread

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;
Run Code Online (Sandbox Code Playgroud)

没有等待IsRunning标志的循环,DoAction将无法成功将消息发布到包含的窗口句柄,因为它尚未创建.基本上,inc(Test)内部WndProc不会被触发.

有没有更好的方法来等待线程启动并在Execute方法内完成必要的初始化,或者这个解决方案是否能够获得好处?

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

Dav*_*nan 9

主线程

  1. 创建一个事件.例如,TSimpleEvent足以满足您的需求.
  2. 将事件设置为无信号.因为TSimpleEvent这是一个电话ResetEvent.我希望新创造的TSimpleEvent会处于无信号状态,但是我不记得那个细节了.
  3. 创建线程,在构造函数中传递事件.
  4. 等待事件发出信号.对于TSimpleEvent这意味着调用WaitFor.

工人线程

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

  • 等待的最佳位置是`DoAction`方法或任何其他公共方法,您必须确保一切正常运行 (3认同)

Sir*_*ufo 8

如果一切准备就绪,请FIsRunning从更改BooleanTEvent发出信号.

现在你可以在任何时候等待这个事件(特别是在公共方法中DoAction):

const
  WM_MY_ACTION = WM_APP + 10;

type
  TWndThread = class(TThread)
  private
    FIsRunning: TEvent; // <- event
  protected
    fWndHandle: THandle;
    procedure WndProc(var Msg: TMessage);
    procedure Execute; override;

    procedure CheckIsRunning; // guard method
  public
    constructor Create;
    destructor Destroy; override;
    procedure DoAction;
  end;

constructor TWndThread.Create;
begin
  // create event
  FIsRunning := TEvent.Create( nil, True, False, '' );
  inherited;
end;

destructor Destroy;
begin
  inherited;
  // free event
  FIsRunning.Free;
end;

procedure CheckIsRunning;
begin
  // guard if terminated
  if Terminated then
    raise Exception.Create( 'Already terminated' );
  // wait for event
  FIsRunning.WaitFor();
end;

procedure TWndThread.Execute;
var
  Msg: TMsg;
begin
  fWndHandle := AllocateHWnd(WndProc);

  // set event
  FIsRunning.SetEvent;

  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
  // guard method
  CheckIsRunning;
  // do the action
  PostMessage(fWndHandle, WM_MY_ACTION, 0, 0);
end;
Run Code Online (Sandbox Code Playgroud)

现在一切都很容易使用,你只需等待,如果有一个特殊的原因等待(访问DoAction方法太快)

var
  t: TWndThread;
begin
  t := TWndThread.Create;
  try
    t.DoAction;
  finally
    t.Free;
  end;
end;
Run Code Online (Sandbox Code Playgroud)