使用取消和终止处理程序正确构造 ParallelTask (IOmniParallelTask)
proper construction of ParallelTask (IOmniParallelTask) with cancellation & termination handler
我只是在阅读文档后尝试使用 OmniThreadLibrary,但在构建 ParallelTask 时我仍然面临一些 simple/early 问题。
在使用 cancellationToken
和 terminationHandler
构建 ParallelTask 之后,terminationHandler.OnTerminated
和 OnStop
在异步执行完成后没有被执行,我无法找出原因:-(
我希望一些 OTL 专业人士可以帮助我解决这个问题。
我想达到的目标:
- 执行异步操作
- 取消时停止执行(取消令牌)
- 异步操作完成后在主线程中执行一些代码(检查异常)
到目前为止我做了什么:
阅读文档后我创建了一个 ParallelTask,通过 TaskConfig
设置了 cancellationToken
和 terminationHandler
并执行了操作。
执行的操作本身会检查 cancellationToken
是否发出信号并执行其工作(此处为 1s 的睡眠)。 HandleOnTerminated
方法检查
错误并设置 fIsDone 和 fHasError 标志,由主线程的某人读取。
unit OTLSetup.Async;
interface
uses
OtlParallel, OtlSync, OtlTaskControl, OtlTask;
type
IAsyncOperation = interface
['{6B10AB46-DEB6-48F5-AC36-E9327AA54C82}']
procedure Execute;
procedure Cancel;
function IsDone: boolean;
end;
TAsyncOperation = class(TInterfacedObject, IAsyncOperation)
protected
fParallelTask: IOmniParallelTask;
fCancellationToken: IOmniCancellationToken;
fIsDone: boolean;
procedure HandleOnTerminated(const task: IOmniTaskControl);
procedure HandleOnStop;
procedure AsyncOperation(const task: IOmniTask);
public
procedure Execute;
procedure Cancel;
function IsDone: boolean;
end;
implementation
uses
Winapi.Windows;
{ TAsyncOperation }
procedure TAsyncOperation.Cancel;
begin
fCancellationToken.Signal;
end;
procedure TAsyncOperation.Execute;
begin
if Assigned(fParallelTask) then
Exit;
fIsDone := false;
fCancellationToken := CreateOmniCancellationToken;
fParallelTask := Parallel.ParallelTask;
fParallelTask.NoWait.NumTasks(1);
fParallelTask.TaskConfig(Parallel.TaskConfig.CancelWith(fCancellationToken).OnTerminated(HandleOnTerminated));
fParallelTask.OnStop(HandleOnStop);
fParallelTask.Execute(AsyncOperation);
end;
procedure TAsyncOperation.AsyncOperation(const task: IOmniTask);
var
I: Integer;
begin
for I := 0 to 5 do
if task.CancellationToken.IsSignalled then
Exit
else
Winapi.Windows.Sleep(1000);
end;
procedure TAsyncOperation.HandleOnStop;
begin
fParallelTask := nil;
fIsDone := true;
end;
procedure TAsyncOperation.HandleOnTerminated(const task: IOmniTaskControl);
begin
fParallelTask := NIL;
fIsDone := true;
end;
function TAsyncOperation.IsDone: boolean;
begin
result := fIsDone;
end;
end.
有了这个和平的代码,fIsDone
永远不会被设置,因为 HandleOnTerminate
和 HandleOnStop
永远不会被调用。所以用上面的例子
以下 ConsoleApplication 似乎永远不会结束:
program OTLSetup;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
OTLSetup.Async in 'OTLSetup.Async.pas';
var
LAsync: IAsyncOperation;
begin
LAsync := TAsyncOperation.Create;
try
LAsync.Execute;
while not LAsync.IsDone do
Writeln('Async task still running');
Writeln('Async task finished');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
如评论中所述,我面临的问题是由控制台应用程序本身引起的,因为它不包含消息循环(在我的例子中是 DUnitX 项目)。
因为 OTL 通信似乎是基于 windowsmessages,所以在没有工作消息循环的情况下不会触发 OnTerminated 和 OnStop。
我只是在阅读文档后尝试使用 OmniThreadLibrary,但在构建 ParallelTask 时我仍然面临一些 simple/early 问题。
在使用 cancellationToken
和 terminationHandler
构建 ParallelTask 之后,terminationHandler.OnTerminated
和 OnStop
在异步执行完成后没有被执行,我无法找出原因:-(
我希望一些 OTL 专业人士可以帮助我解决这个问题。
我想达到的目标:
- 执行异步操作
- 取消时停止执行(取消令牌)
- 异步操作完成后在主线程中执行一些代码(检查异常)
到目前为止我做了什么:
阅读文档后我创建了一个 ParallelTask,通过 TaskConfig
设置了 cancellationToken
和 terminationHandler
并执行了操作。
执行的操作本身会检查 cancellationToken
是否发出信号并执行其工作(此处为 1s 的睡眠)。 HandleOnTerminated
方法检查
错误并设置 fIsDone 和 fHasError 标志,由主线程的某人读取。
unit OTLSetup.Async;
interface
uses
OtlParallel, OtlSync, OtlTaskControl, OtlTask;
type
IAsyncOperation = interface
['{6B10AB46-DEB6-48F5-AC36-E9327AA54C82}']
procedure Execute;
procedure Cancel;
function IsDone: boolean;
end;
TAsyncOperation = class(TInterfacedObject, IAsyncOperation)
protected
fParallelTask: IOmniParallelTask;
fCancellationToken: IOmniCancellationToken;
fIsDone: boolean;
procedure HandleOnTerminated(const task: IOmniTaskControl);
procedure HandleOnStop;
procedure AsyncOperation(const task: IOmniTask);
public
procedure Execute;
procedure Cancel;
function IsDone: boolean;
end;
implementation
uses
Winapi.Windows;
{ TAsyncOperation }
procedure TAsyncOperation.Cancel;
begin
fCancellationToken.Signal;
end;
procedure TAsyncOperation.Execute;
begin
if Assigned(fParallelTask) then
Exit;
fIsDone := false;
fCancellationToken := CreateOmniCancellationToken;
fParallelTask := Parallel.ParallelTask;
fParallelTask.NoWait.NumTasks(1);
fParallelTask.TaskConfig(Parallel.TaskConfig.CancelWith(fCancellationToken).OnTerminated(HandleOnTerminated));
fParallelTask.OnStop(HandleOnStop);
fParallelTask.Execute(AsyncOperation);
end;
procedure TAsyncOperation.AsyncOperation(const task: IOmniTask);
var
I: Integer;
begin
for I := 0 to 5 do
if task.CancellationToken.IsSignalled then
Exit
else
Winapi.Windows.Sleep(1000);
end;
procedure TAsyncOperation.HandleOnStop;
begin
fParallelTask := nil;
fIsDone := true;
end;
procedure TAsyncOperation.HandleOnTerminated(const task: IOmniTaskControl);
begin
fParallelTask := NIL;
fIsDone := true;
end;
function TAsyncOperation.IsDone: boolean;
begin
result := fIsDone;
end;
end.
有了这个和平的代码,fIsDone
永远不会被设置,因为 HandleOnTerminate
和 HandleOnStop
永远不会被调用。所以用上面的例子
以下 ConsoleApplication 似乎永远不会结束:
program OTLSetup;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
OTLSetup.Async in 'OTLSetup.Async.pas';
var
LAsync: IAsyncOperation;
begin
LAsync := TAsyncOperation.Create;
try
LAsync.Execute;
while not LAsync.IsDone do
Writeln('Async task still running');
Writeln('Async task finished');
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
如评论中所述,我面临的问题是由控制台应用程序本身引起的,因为它不包含消息循环(在我的例子中是 DUnitX 项目)。
因为 OTL 通信似乎是基于 windowsmessages,所以在没有工作消息循环的情况下不会触发 OnTerminated 和 OnStop。