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
答案 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
。
修改
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;
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
必须安排回主线程,就像原始代码一样。