【C# Tips】非同期処理(Task)の例外処理を極めて、障害を正しく検知しよう!!

2020年9月3日木曜日

C# Task

t f B! P L

enter image description here

C#のTaskの例外処理ちゃんとやってますか??

C# で非同期処理を実装する場合、Taskクラスを使っている方が多いと思います。Taskクラスを使えば、以下のように、たった数行のコードで簡単に非同期処理を作る事が出来ます。

Task.Run(()  =>  Console.WriteLine("何やら重たい処理..."));

しかし、このコードには重大な問題があります。
例外処理がまったく出来てません。

Task内で発生した例外は、適切に処理しないと、例外がキャッチされずに正常終了してしまいます。

「それでもいい!」と言うのであれば別ですが、通常は例外が発生したら、ユーザに通知 or ログ出力等を行うと思います。

今回は、非同期処理内で発生した例外を、適切に処理する方法について紹介したいと思います。

Task で発生した例外の処理方法

1. Wait() で Taskの終了を待機する場合

Task.Wait() で Taskの終了を待機する場合、Wait() 関数の部分を try ~ catch で囲めば、Task内で発生した例外を拾う事が出来ます。


//例外を発生させる Task を作成  
Task task = new Task(() => {  
    throw new Exception("エラーが発生しました!!");  
});  
task.Start();  
  
// Wait()でタスクの終了を待つ  
try  {  
    task.Wait();  
} catch (AggregateException e)  {  
    //タスク内で発生した例外は、ここでキャッチできる  
    Console.WriteLine(e.InnerException.Message);  
}
POINT Task内で発生した例外は、AggregateExceptionにラップされて、親スレッドにスローされます。
その為、実際に発生した例外を取得するには、AggregateException.InnerExceptionプロパティより発生した例外を取得します。

スポンサーリンク

2. async / await を使用する場合

async / await パターンの場合、通常の例外と同じように、try ~ catch で非同期の例外処理を行う事ができます。
AggregateExceptionで例外がラップされる事もない為、ホントに普通に書けます。
正直、こんなに簡潔に非同期の例外処理が書けるなんて、衝撃でした。

public async Task Sample() {

    try {
        //例外を発生させる Task を作成
        await Task.Run(() => {
            throw new ApplicationException("エラーが発生しました!!");
        });
    } catch (ApplicationException e) {
        Console.WriteLine(e.Message);
    }
}

3. 突き放し型の場合

これまでとは違い、Taskの終了を待機しない突き放し型の場合についてです。
この場合Task.ContinueWith()関数を使用して、下のコードの様に、Task終了時に例外が発生しているかチェックします。

//例外を発生させる Task を作成
Task task = new Task(() => {
    throw new Exception("エラーが発生しました!!");
});
task.Start();

//タスク完了時の処理
task.ContinueWith((t) => {
    if (t.Exception == null) {
        //通常の処理
    } else {
        //タスク内でエラーが発生した時の処理
        Console.WriteLine(t.Exception.InnerException);
    }
});

まとめ

Task の非同期処理内で発生した例外は、簡単なコードで検知する事ができます。
正しく処理を行って、障害をすばやく検知できるシステムを作りたいですね。。。

スポンサーリンク

QooQ