为什么 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.
我在Initialize
和DoOnTimer
中设置了断点。前者执行得很好,但后者根本不会被调用。顺便说一句:Cleanup
都没有被调用,所以任务仍然是 运行.
我做错了什么?在 OTL 任务中不能使用 TTimer
吗?如果是,为什么?
更新: 我为 TTimer
() 找到了 workaround 但为什么 TTimer
方法不起作用?
您基于 TTimer
的代码不起作用,因为 TTimer
使用 windows 消息触发计时器事件,并且 windows 消息未在 OTL worker 中处理默认情况下。
在 .Run
之前调用 .MsgWait
,内部工作循环将使用 MsgWaitForMultipleObjects
而不是 WaitForMultipleObjects
,这将允许消息处理。
也就是说,你真的不应该在后台任务中使用 TTimer
因为 - 正如其他人所说 - TTimer
不是线程安全的。
我想在另一个线程中运行的 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.
我在Initialize
和DoOnTimer
中设置了断点。前者执行得很好,但后者根本不会被调用。顺便说一句:Cleanup
都没有被调用,所以任务仍然是 运行.
我做错了什么?在 OTL 任务中不能使用 TTimer
吗?如果是,为什么?
更新: 我为 TTimer
() 找到了 workaround 但为什么 TTimer
方法不起作用?
您基于 TTimer
的代码不起作用,因为 TTimer
使用 windows 消息触发计时器事件,并且 windows 消息未在 OTL worker 中处理默认情况下。
在 .Run
之前调用 .MsgWait
,内部工作循环将使用 MsgWaitForMultipleObjects
而不是 WaitForMultipleObjects
,这将允许消息处理。
也就是说,你真的不应该在后台任务中使用 TTimer
因为 - 正如其他人所说 - TTimer
不是线程安全的。