IOmniParallelTask​​异常处理无法正常工作

时间:2015-12-12 23:49:11

标签: delphi omnithreadlibrary

IOmniParallelTask执行中未处理的异常应该(我理解文档)被OTL捕获并附加到IOmniTaskControl实例,termination handler可以从IOmniTaskConfig访问1}}。

所以在使用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

1 个答案:

答案 0 :(得分: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在调用nil时已经HandleOnTaskThreadTerminated

修改

DetachExceptionFatalExceptionIsExceptional已添加到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;

EDIT2

如评论中所述,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必须安排回主线程,就像原始代码一样。