使用取消和终止处理程序正确构造 ParallelTask​​ (IOmniParallelTask​​)

proper construction of ParallelTask (IOmniParallelTask) with cancellation & termination handler

我只是在阅读文档后尝试使用 OmniThreadLibrary,但在构建 ParallelTask​​ 时我仍然面临一些 simple/early 问题。

在使用 cancellationTokenterminationHandler 构建 ParallelTask​​ 之后,terminationHandler.OnTerminatedOnStop 在异步执行完成后没有被执行,我无法找出原因:-(

我希望一些 OTL 专业人士可以帮助我解决这个问题。

我想达到的目标:

到目前为止我做了什么:

阅读文档后我创建了一个 ParallelTask​​,通过 TaskConfig 设置了 cancellationTokenterminationHandler 并执行了操作。 执行的操作本身会检查 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 永远不会被设置,因为 HandleOnTerminateHandleOnStop 永远不会被调用。所以用上面的例子 以下 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。