3

I am just playing around with the OmniThreadLibrary after reading the docs, but I am still facing some simple/early problems on constructing a ParallelTask.

After the construction of a ParallelTask with cancellationToken and terminationHandler, terminationHandler.OnTerminated and OnStop are not being executed after the async execution is done and I was not able to find out why :-(

I hope some of the OTL pros can help me out on this one.

What I want to achieve:

  • execute an asynchronous operation
  • stop execution when cancelled (cancellationtoken)
  • execute some code in the mainthread (checking for exceptions) when the async operation is done

What I did so far:

After reading the docs I created a ParallelTask, setting up cancellationToken and terminationHandler via TaskConfig and executed the operation. The executed operation itself checks for the cancellationToken being signalled and does its work (here a Sleep of 1s). The HandleOnTerminated method checks for errors and sets the fIsDone and fHasError flags, getting read by someone from mainthread.

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.

With this peace of Code, fIsDone is never set, because HandleOnTerminate and HandleOnStopare never called. So with the exmaple from above the following ConsoleApplication seems to never end:

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.
4

1 回答 1

2

正如评论中所发布的,我面临的问题是由控制台应用程序本身引起的,因为它不包含消息循环(在我的情况下是 DUnitX 项目)。

因为 OTL 通信似乎是基于 windowsmessages,所以 OnTerminated 和 OnStop 在没有工作消息循环的情况下不会被触发。

于 2015-12-01T19:52:35.430 回答