3

执行中未处理的异常IOmniParallelTask应该(据我了解文档)由 OTL 捕获并附加到IOmniTaskControl实例,该实例可以由termination handlerfrom访问IOmniTaskConfig

所以在用这样的设置IOmniParallelTask实例之后termination handler

fTask := Parallel.ParallelTask.NoWait.NumTasks(1);
fTask.OnStop(HandleOnTaskStop);

fTask.TaskConfig(Parallel.TaskConfig.OnTerminated(HandleOnTaskThreadTerminated));
fTask.Execute(TaskToExecute);

内的任何未处理的异常TaskToExecute

procedure TFormMain.TaskToExecute;
begin
  Winapi.Windows.Sleep(2000);
  raise Exception.Create('async operation exeption');
end;

应该附加到IOmniTaskControl您在以下范围内获得的实例termination handler

procedure TFormMain.HandleOnTaskThreadTerminated(const task: IOmniTaskControl);
begin
  if not Assigned(task.FatalException) then
    Exit;

  memo.Lines.Add('an exception occured: ' + task.FatalException.Message);
end;

此时的问题是,没有分配异常IOmniTaskControl.FatalException我不知道为什么。

也许你们中的一些人对我做错了什么有一些想法。整个 VCL 示例项目可以在这里找到:https ://github.com/stackoverflow-samples/OTLTaskException

4

1 回答 1

3

这是一个抽象层问题。将线程代码异常存储在与属性Parallel.ParallelTask不同步的本地字段中。IOmniTaskControl.FatalException(我同意这不是一个好的行为,但我还不确定解决这个问题的最佳方法是什么。)

目前,访问捕获的IOmniParallelTask对象异常的唯一方法是调用其WaitFor方法。IOmniParallelTask应该真正暴露FatalException/DetachException对,就像IOmniParallelJoin. (再次,一个疏忽,应该在未来修复。)

解决当前 OTL 问题的最佳方法是调用WaitFor终止处理程序并在那里捕获异常。

procedure TFormMain.HandleOnTaskThreadTerminated(const task: IOmniTaskControl);
begin
  try
    fTask.WaitFor(0);
  except
    on E: Exception do
      memo.Lines.Add('an exception occured: ' + E.Message);
  end;
  CleanupTask;
end;

我还删除了HandleOnTaskStop并将清理移动到终止处理程序。否则,在当时fTask就已经被叫了。nilHandleOnTaskThreadTerminated


编辑

DetachException, FatalException, 并且IsExceptional已经被添加到了,IOmniParallelTask所以现在你可以简单地做你想做的事情(除了你必须使用fTask, 而不是task)。

procedure TFormMain.HandleOnTaskThreadTerminated(const task: IOmniTaskControl);
begin
  if not assigned(fTask.FatalException) then
    Exit;

  memo.Lines.Add('an exception occured: ' + FTask.FatalException.Message);
  CleanupTask;
end;

编辑2

如评论中所述,OnTerminate处理程序与一项任务相关。在此示例中,这不是问题,因为代码确保只有一个后台任务正在运行 ( NumTasks(1))。

但是,在一般情况下,OnStop应为此目的使用处理程序。

procedure TFormMain.btnExecuteTaskClick(Sender: TObject);
begin
  if Assigned(fTask) then
    Exit;

  memo.Lines.Add('task has been started..');
  fTask := Parallel.ParallelTask.NoWait.NumTasks(1);
  fTask.OnStop(HandleOnStop);
  fTask.Execute(TaskToExecute);
end;

procedure TFormMain.HandleOnStop;
begin
  if not assigned(fTask.FatalException) then
    Exit;

  memo.Lines.Add('an exception occured: ' + FTask.DetachException.Message);
  TThread.Queue(nil, CleanupTask);
end;

由于HandleOnStop在后台线程中被调用(因为NoWait被使用),CleanupTask必须被调度回主线程,就像在原始代码中一样。

于 2015-12-13T20:26:05.337 回答