タスクの継続中に例外を適切に処理する方法 質問する

タスクの継続中に例外を適切に処理する方法 質問する

次のコードをご覧ください。

static void Main(string[] args)
{
    // Get the task.
    var task = Task.Factory.StartNew<int>(() => { return div(32, 0); });

    // For error handling.
    task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); }, 
        TaskContinuationOptions.OnlyOnFaulted);

    // If it succeeded.
    task.ContinueWith(t => { Console.WriteLine(t.Result); }, 
        TaskContinuationOptions.OnlyOnRanToCompletion);
    Console.ReadKey();
    Console.WriteLine("Hello");
}

private static int div(int x, int y)
{
    if (y == 0)
    {
        throw new ArgumentException("y");
    }
    return x / y;
}

リリースモードでコードを実行すると、出力は「1つ以上のエラーが発生しました」となり、「Enterキー」を押すと「Hello」も表示されます。デバッグモードでコードを実行すると、出力はリリースモードと同じです。しかし、IDEでデバッグする場合、コントロールが行を実行するとIDE例外メッセージ(「ユーザーコードで未処理の例外」)が表示されます。

throw new ArgumentException("y"); 

そこから続行すると、プログラムはクラッシュせず、リリース モードと同じ出力が表示されます。これは例外を処理する適切な方法でしょうか?

ベストアンサー1

OnlyOnFaultedおそらく、個別のおよびハンドラーは必要なくOnlyOnRanToCompletion、 を処理していませんOnlyOnCanceled。チェックこの答え詳細については。

しかし、IDEでデバッグする場合、コントロールが次の行を実行すると、IDE例外メッセージ(「ユーザーコードで未処理の例外」)が表示されます。

おそらくデバッグ/例外オプション ( Ctrl+ Alt+ E) で例外を有効にしているため、デバッガーの下に例外が表示されます。

そこから続行すると、プログラムはクラッシュせず、リリース モードと同じ出力が表示されます。これは例外を処理する適切な方法でしょうか?

アクション内でスローされたが処理されなかった例外は、Task自動的に再スローされません。代わりに、将来の観察のためにTask.Exception(型AggregateException) としてラップされます。元の例外には次のようにアクセスできますException.InnerException

Exception ex = task.Exception;
if (ex != null && ex.InnerException != null)
    ex = ex.InnerException;

この場合、プログラムをクラッシュさせるには、実際に例外を観察する必要があります。タスクアクション、例えば以下を参照するTask.Result:

static void Main(string[] args)
{
    // Get the task.
    var task = Task.Factory.StartNew<int>(() => { return div(32, 0); });

    // For error handling.
    task.ContinueWith(t => { Console.WriteLine(t.Exception.Message); }, 
        TaskContinuationOptions.OnlyOnFaulted);

    // If it succeeded.
    task.ContinueWith(t => { Console.WriteLine(t.Result); }, 
        TaskContinuationOptions.OnlyOnRanToCompletion);

    Console.ReadKey();

    Console.WriteLine("result: " + task.Result); // will crash here

    // you can also check task.Exception

    Console.WriteLine("Hello");
}

詳細:タスクと未処理の例外.NET 4.5 でのタスク例外処理

コメントに対応するために更新しました:.NET 4.0 と VS2010 を使用した UI アプリでこれを行う方法は次のとおりです。

void Button_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew<int>(() => 
    {
        return div(32, 0); 
    }).ContinueWith((t) =>
    {
        if (t.IsFaulted)
        {
            // faulted with exception
            Exception ex = t.Exception;
            while (ex is AggregateException && ex.InnerException != null)
                ex = ex.InnerException;
            MessageBox.Show("Error: " + ex.Message);
        }
        else if (t.IsCanceled)
        {
            // this should not happen 
            // as you don't pass a CancellationToken into your task
            MessageBox.Show("Canclled.");
        }
        else
        {
            // completed successfully
            MessageBox.Show("Result: " + t.Result);
        }
    }, TaskScheduler.FromCurrentSynchronizationContext());
}

.NET 4.0をターゲットにし、監視されていない例外に対して.NET 4.0の動作(つまり、タスクがガベージコレクションされたときに再スローする)が必要な場合は、明示的に以下で設定しますapp.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
  <runtime>
    <ThrowUnobservedTaskExceptions enabled="true"/>
  </runtime>
</configuration>

詳細については、こちらをご覧ください:

.NET4 における監視されていないタスク例外

おすすめ記事