3

実行中の未処理の例外IOmniParallelTaskは、(ドキュメントを理解しているように) OTL によってキャッチされ、インスタンスにアタッチされ、 fromIOmniTaskControlによってアクセスされる可能性があります。termination handlerIOmniTaskConfig

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を実際に公開する必要があります。(繰り返しますが、将来修正されるはずの見落としです。)FatalExceptionDetachExceptionIOmniParallelJoin

現在の 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


編集

DetachExceptionFatalException、および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

コメントで述べたように、OnTerminatehandler は 1 つのタスクに関連しています。この例では、バックグラウンド タスクが 1 つだけ実行されていることをコードが確認しているため、これは問題ではありません ( 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 に答える