I wanted to realize a repetitive task in an OmniThreadLibrary worker task, that runs in another thread. The task should be executed every 3 seconds, for example.
Therefore I wrote a TOmniWorker
descendant with an instance of TTimer
as you can see below:
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.
I set breakpoints in Initialize
and DoOnTimer
. Former executes well but latter won't be called at all. BTW: Cleanup
isn't called neither, so the task is still running.
What am I doing wrong? Is it impossible to use a TTimer
in an OTL task? If yes, why?
UPDATE: I found a workaround for TTimer
() but why does TTimer
approach not work?
You TTimer
-based code doesn't work because TTimer
uses windows messages to trigger the timer event and windows messages are not processed in an OTL worker by default.
Call .MsgWait
before .Run
and internal worker loop will use MsgWaitForMultipleObjects
instead of WaitForMultipleObjects
which will allow for message processing.
Saying that, you really should not use TTimer
in background tasks because - as others have said - TTimer
is not threadsafe.