IOmniParallelTask 的异常处理不起作用
Exceptionhandling with IOmniParallelTask not working
IOmniParallelTask
执行中未处理的异常应该(据我了解文档)被 OTL 捕获并附加到 IOmniTaskControl
实例,termination handler
可以从IOmniTaskConfig
.
因此,在使用 termination handler
设置 IOmniParallelTask
实例后,如下所示:
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;
应该附加到您在 termination handler
:
中获得的 IOmniTaskControl
实例
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/Whosebug-samples/OTLTaskException
这是一个抽象层问题。 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
并将清理移至终止处理程序。否则,在调用 HandleOnTaskThreadTerminated
时 fTask
已经是 nil
。
编辑
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
必须像原来的代码一样调度回主线程。
IOmniParallelTask
执行中未处理的异常应该(据我了解文档)被 OTL 捕获并附加到 IOmniTaskControl
实例,termination handler
可以从IOmniTaskConfig
.
因此,在使用 termination handler
设置 IOmniParallelTask
实例后,如下所示:
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;
应该附加到您在 termination handler
:
IOmniTaskControl
实例
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/Whosebug-samples/OTLTaskException
这是一个抽象层问题。 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
并将清理移至终止处理程序。否则,在调用 HandleOnTaskThreadTerminated
时 fTask
已经是 nil
。
编辑
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
必须像原来的代码一样调度回主线程。