9

TPL/タスクの使用内部のtry/catchステートメントを使用して例外処理を実行できます。

  Task.Factory.StartNew(
    ()=>
      {
        try
        {
          // Do stuff
        }
        catch
        {
          // Handle exception
        }
      });

または、次のように、ContinueWithを使用します。

Task.Factory.StartNew(
    ()=>
      {
          // Do stuff
      }).ContinueWith(
        task =>
        {
          if(task.Exception != null)
            // Handle exception
        });

どちらの方法を使用することをお勧めしますか?それぞれの短所と長所は何ですか?

4

5 に答える 5

3

タスク自体がスローしているメソッド内で例外を適切に処理できる場合は、やむを得ない理由がない限り、継続ではなく、最初のタスク内で例外をキャッチする必要があります。タスク自体と同じスコープで継続を作成すると(2番目の例で行われるように)、不必要に作業が追加されます。

タスクが定義されている場所とはまったく異なるスコープから例外が処理される場合、継続で例外を処理することは有用または必要です。たとえば、任意のタスクが与えられたメソッドがあり、そのタスクの定義が何であるかわからない場合、コードが例外をスローした場合に何かを行う必要があります。例外を処理する継続。

例外を処理する継続がある場合は、継続の定義内でチェックを行うのではなく、タスクが例外をスローしたときにのみ継続を実行するために使用できることに注意TaskContinuationOptions.OnlyOnFaultedてください。

于 2013-02-13T16:04:32.910 に答える
3

それは主にあなたのデザインが必要とするものにかかっています。考慮すべきいくつかの事柄:

それらをスローするタスクで例外をキャッチします

  • タスクが、特定の例外タイプの後のクリーンアップを含む、分割できない作業単位を表す場合。
  • 特定の例外タイプが何らかの理由でタスクの外部に伝播されない場合。たとえば、クライアントコードのコントラクトの期待に応えるために、別のタイプの外部例外でラップする必要があります。

継続で例外を処理する

  • 例外のクリーンアップを別の方法でスケジュールする必要がある場合TaskScheduler。たとえば、スレッドプールで「プライマリ」タスクを実行し、すべての例外ログをUIスレッドにマーシャリングする場合。
  • 複数の継続を使用することが理にかなっている場合は、例外を除いてそれぞれが異なることを行いますが、それは少し珍しいことです。
  • Taskコードを指定しないsからの例外が監視され、適切に処理されるようにするため。たとえば、によって作成されたタスクの後に適切にクリーンアップするTaskFactory.FromAsync。状況にもよりますが、それはを待つことによっても行うことができますTask
于 2013-02-13T14:50:27.187 に答える
1

ある程度、特にタスクコードと呼び出し元コードを「所有」している場合は、好みの問題です。考慮すべき点がいくつかあります。

まず、処理方法を知っている例外のみをキャッチする必要があります。これは、継続で処理する場合でも、アクション内でtry/catchを使用して処理する場合でも適用されます。

キャッチされない例外に関する.NET4.5の動作の変更にも注意してください。この変更は、「純粋な」アプローチ(キャッチされていないタスクの例外でプロセスを破棄する)から、それほど厳しくないアプローチに効果的になります。それでも、意図的に新しい動作に依存するのは良くありません。

2つの選択肢のどちらを優先するかについては、2番目の選択肢を選択するための議論があります。継続で例外を処理することです。 .NETでは、メソッドが。を返すことがますます一般的になりますTask。たとえば、Stream.ReadAsync。このようなメソッドを正しく使用するには、継続が必要です(従来の方法、または新しいawait機能でtry / catchブロックを使用します。これは同じことですが、コーディングと読み取りがはるかに簡単です)。したがって、明示的に別のことを知らない限り、いずれかが失敗する可能性があると想定し、適切な例外処理動作をコーディングする習慣を身に付けることをお勧めします。Task

興味がある場合は、.NET4.5で2番目の例をコーディングする別の方法を次に示します。

async Task MyMethod()
{
    try
    {
        await Task.Run(
            () =>
            {
                // Some work.
            });
    }
    catch (SomeException ex)
    {
    }
}

もう1つの違いは、コードがUIスレッドから呼び出されるWindowsフォームまたはWPFアプリケーションに最も頻繁に当てはまります。ここで、を使用する場合のTPLのデフォルトの動作はawait、UIスレッドにマーシャルする同期コンテキストを使用して継続を実行することです。つまりTask.Run、UIスレッドから呼び出された場合、継続はUIスレッドでも実行されます。

これは、例外に応答してユーザーにダイアログを表示する場合に役立ちます。Taskworload内からそれを正常に行うことはできません。ではなく明示的な継続を使用する場合は、 TaskScheduler.FromCurrentSynchronizationContextを使用して作成されたものをContinueWithの適切なオーバーロードにawait渡す必要があります。TaskScheduler

于 2013-02-13T14:03:02.170 に答える
1

2つの例は概念的に異なります。

1つ目は、実行中のタスクの内部で例外を処理します。catchの後に実行されているコードは、引き続き実行されます。

2つ目は、最初のタスクが終了した後にスケジューラーによって常に実行される別の非同期タスクをスケジュールします。

答えは、あなたが達成しようとしていることによって異なります-明確な答えはありません-しかし、2番目はtplに沿ったものです。

また、2番目の例では、task.IsFaultedはそのtask.Exceptionをより明確にしています。

于 2013-02-13T14:44:43.373 に答える
-1

文脈にもよると思います。オリーが言ったように、処理方法を知っている例外のみを処理する必要があります。例外の処理方法を知っている場合は、例外を処理する必要があると言います。

たとえば、ファイルからデータをロードするか、デフォルト値(例外をスローする可能性があります)にフォールバックする必要があるタスクがある場合、その1つの方法は次のようになります(擬似コード)。

Task.Factory.StartNew(()=>
{
    MyObject objectToSet = null;
    try
    {
        objectToSet = File.Open("mydata");
    }
    catch (FileException ex)
    {
        // this will catch the FileException because we know how to handle that!

        // the following will however throw an exception that we cannot handle
        objectToSet = GetFallBackValue(); 
    }
    // when we are here we promise that the objectToSet is valid.
});

の場合、File.Open続行する方法を知っています。私たちの場合はそうでGetFallBackValue()はないので、それを呼び出し元に伝播し、一貫性のない状態にあることを示します。

于 2013-02-13T14:31:07.837 に答える