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);
}
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 の非同期処理内で発生した例外は、簡単なコードで検知する事ができます。
正しく処理を行って、障害をすばやく検知できるシステムを作りたいですね。。。
.NETも、.NET Frameworkと.NET Coreが統合され、.NET 5として集約されたが、この記事で紹介しているTaskの例外処理については、新しい .NET 5でも大きく変わっておらずそのまま使える。
0 件のコメント:
コメントを投稿