为什么 TTimer 不能在 OTL worker 任务中工作?

Why won't TTimer work in OTL worker task?

我想在另一个线程中运行的 OmniThreadLibrary 工作任务中实现重复任务。例如,该任务应每 3 秒执行一次。 因此,我写了一个 TOmniWorker 后代,其中有一个 TTimer 的实例,如下所示:

program Project14;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Vcl.ExtCtrls,
  Vcl.Forms,
  OtlTaskControl;

type
  TMyTimerWorker = class(TOmniWorker)
  strict private
    FTimer: TTimer;
    procedure DoOnTimer(Sender: TObject);
  protected
    function Initialize: Boolean; override;
    procedure Cleanup; override;
  end;

{ TMyTimerWorker }

procedure TMyTimerWorker.Cleanup;
begin
  FTimer.Free;
  inherited;
end;

procedure TMyTimerWorker.DoOnTimer(Sender: TObject);
begin
  Beep;
end;

function TMyTimerWorker.Initialize: Boolean;
begin
  Result := inherited;
  if not Result then exit;

  FTimer := TTimer.Create(nil);
  FTimer.OnTimer  := DoOnTimer;
  FTimer.Interval := 3000;
  FTimer.Enabled  := True; // note: this isn't necessary, but is added to avoid hints that 'Enabled' might be 'False'
end;

var
  LTimerWorker: IOmniWorker;
begin
  try
    LTimerWorker := TMyTimerWorker.Create;
    CreateTask(LTimerWorker).Unobserved.Run;
    while True do
      Application.ProcessMessages;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

我在InitializeDoOnTimer中设置了断点。前者执行得很好,但后者根本不会被调用。顺便说一句:Cleanup 都没有被调用,所以任务仍然是 运行.

我做错了什么?在 OTL 任务中不能使用 TTimer 吗?如果是,为什么?

更新: 我为 TTimer () 找到了 workaround 但为什么 TTimer 方法不起作用?

您基于 TTimer 的代码不起作用,因为 TTimer 使用 windows 消息触发计时器事件,并且 windows 消息未在 OTL worker 中处理默认情况下。

.Run 之前调用 .MsgWait,内部工作循环将使用 MsgWaitForMultipleObjects 而不是 WaitForMultipleObjects,这将允许消息处理。

也就是说,你真的不应该在后台任务中使用 TTimer 因为 - 正如其他人所说 - TTimer 不是线程安全的。