8

私はこの簡単なコードを持っています:

var g=  Task.Factory.StartNew<int> (() => 8)
       .ContinueWith (ant =>{throw null;})
       .ContinueWith (a =>{ Console.WriteLine("OK");},TaskContinuationOptions.NotOnFaulted);

 try{
      Console.WriteLine("1");
      g.Wait();
      Console.WriteLine("2");
     }

catch (AggregateException  ex)
      {Console.WriteLine("catch"); }

出力 :

1
catch
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

msdn:

TaskContinuationOptions.NotOnFaulted

継続元が未処理の例外をスローした場合、継続タスクをスケジュールしないように指定します。このオプションは、マルチタスクの継続には無効です。

わかった 。

ここに画像の説明を入力

そして、それは問題ありません-この行を表示しないと、前の行のDIDスロー例外が発生します。

質問:

  • プロパティAggregateExceptionを検査していないため、例外が発生しますか?Exception

  • 前件が (各行で) 例外をスローするかどうかを常に検査する必要がありますか? (一行一行確認できない!意味がわからないし面倒くさい

  • try catchブロックは例外を飲み込むべきではありませんでしたか? (私はすべての例外がwaitメソッドにバブルアップすると思っていました....そうですか?)

4

3 に答える 3

5

プロパティAggregateExceptionを検査していないため、例外が発生しますか?Exception

いいえ、gTPL によってタスクがキャンセルされるため、例外が発生します (msdn が述べているように、先行タスクが例外をスローした場合、このタスクはスケジュールされないため)。

ここには 3 つのタスクがあります。

  1. 元のタスク (StartNew を使用する)
  2. 最初の継続タスク (例外をスローする)
  3. 2 番目の継続タスク (OK を出力) (これはコードの g タスクです)。

問題は、 2 番目のタスクが正常に終了した場合にのみ、 TPL に 3 次元タスクを開始するように依頼することです。つまり、この条件が満たされない場合、TPL は新しく作成されたタスクを完全にキャンセルします。

監視しない一時的なタスク (私のリストのタスク 2) があるため、監視されていないタスクの例外が発生しました。フォルト状態を観察することはないため、ファイナライザーをスローしてそれについて通知します。

これは、catch ブロックでタスクのステータスを出力することで確認できます。

catch (AggregateException ex)
{ 
    Console.WriteLine("catch");
    // Will print: Status in catch: Canceled
    Console.WriteLine("Status in catch: {0}", g.Status);
}

前件が (各行で) 例外をスローするかどうかを常に検査する必要がありますか? (一行一行確認できない!意味が分からなくて面倒くさい)

はい、この問題を回避するには、先行タスクの例外を観察する必要があります。

static class TaskEx
{
    public static Task ObserverExceptions(this Task task)
    {
        task.ContinueWith(t => { var ignore = t.Exception; },
                            TaskContinuationOptions.OnlyOnFaulted);
        return task;
    }
}

そして、次のように使用します。

var g=  Task.Factory.StartNew<int> (() => 8)
       .ContinueWith (ant =>{throw null;})
       .ObserveExceptions()
       .ContinueWith (a =>{ Console.WriteLine("OK");});

 try{
      Console.WriteLine("1");
      g.Wait();
      Console.WriteLine("2");
     }

catch (AggregateException  ex)
      {Console.WriteLine("catch"); }

更新: 最後の箇条書きに解決策を追加

try catch ブロックは例外を飲み込むべきではありませんでしたか? (私はすべての例外がwaitメソッドにバブルアップすると思っていました....そうですか?)

TransformWith私たちのプロジェクトには、この特定の問題を解決して以下を得ることができる一連の拡張メソッド ( と呼ばれる) があります。

  1. 例外は catch ブロックにバブルアップし、
  2. アプリケーションをクラッシュさせませんTaskUnobservedException

使い方はこちら

var g = Task.Factory.StartNew(() => 8)
       .ContinueWith(ant => { throw null; })
       // Using our extension method instead of simple ContinueWith
       .TransformWith(t => Console.WriteLine("OK"));

try
{
    Console.WriteLine("1");
    // Will fail with NullReferenceException (inside AggregateExcpetion)
    g.Wait();
    Console.WriteLine("2");
}

catch (AggregateException ex)
{
    // ex.InnerException is a NullReferenceException
    Console.WriteLine(ex.InnerException);
}

そして、ここに拡張メソッドがあります:

static class TaskEx
{
    public static Task TransformWith(this Task future, Action<Task> continuation)
    {
        var tcs = new TaskCompletionSource<object>();
        future
            .ContinueWith(t =>
            {
                if (t.IsCanceled)
                {
                    tcs.SetCanceled();
                }
                else if (t.IsFaulted)
                {
                    tcs.SetException(t.Exception.InnerExceptions);
                }
                else
                {
                    try
                    {
                        continuation(future);
                        tcs.SetResult(null);
                    }
                    catch (Exception e)
                    {
                        tcs.SetException(e);
                    }
                }
            }, TaskContinuationOptions.ExecuteSynchronously);

        return tcs.Task;
    }    
}
于 2012-12-06T09:48:43.790 に答える
3
  • Exception プロパティを調べていないため、AggregateException 例外が発生しますか?

タスクは常に AggregateException をスローします: http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx

次を使用して元の例外を取得できます。

var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
  • 前件が (各行で) 例外をスローするかどうかを常に検査する必要がありますか? (一行一行確認できない!意味が分からなくて面倒くさい)

はい、面倒です。例外をチェックするために、タスクごとに 2 つの継続を作成する必要がありTaskContinuationOptions.OnlyOnFaultedますTaskContinuationOptions.OnlyOnRanToCompletion。必要に応じて、キャンセルに対処するために 3 つ目の継続を作成することも必要です。

  • try catch ブロックは例外を飲み込むべきではありませんでしたか? (私はすべての例外がwaitメソッドにバブルアップすると思っていました....そうですか?)

いいえ、例外はより高いレベルでスローされませんTaskContinuationOptions.OnlyOnFaulted。タスクの継続で使用して、例外があったかどうかを確認する必要があります。.net 4 では使用できないasyncキーワードを使用してのみ、呼び出し元のレベルでタスクの例外を取得できます

于 2012-12-06T09:51:51.153 に答える
0

次のようにAggregateExceptionsを処理します。

catch(AggregateException aex)
{
    aex.Handle(ex =>
    {
       // Do some handling and logging
        return true;
    }
}
于 2012-12-06T18:21:37.650 に答える