async / await の例外処理にハマった話....

2021年7月16日金曜日

C#

t f B! P L

つい先日、C#で非同期処理を簡潔に書ける async, awaitを使った処理の例外処理でハマった。。。

具体的には、asyncで書いた非同期処理でも、waitで待ち合わせてを行った以降は、非同期処理内で発生した例外はすべて AggregateException にラップされるという話だ。

async / await の例外処理の基本

async / await は、非同期の処理を同期処理かのように書けるようにしたC#の構文であるため、例外処理についても、特に意識せずに発生した例外を try ~ catchでハンドリングすることが可能である。

例えば、次のようなコードの場合、非同期処理である DoAsyncTask メソッド内で発生した ApplicationException は、呼び出し元で普通に try ~ catchApplicationExceptionをハンドリングすればキャッチ可能である。

public static async Task Sample() {
    
    try {
        await DoAsyncTask();
    } catch (ApplicationException) {
        Console.WriteLine("ApplicationExceptionが発生");
    } catch (Exception ex) {
        Console.WriteLine("その他の例外が発生 " + ex.GetType().Name);
    }
}

public static async Task DoAsyncTask() {

    // 非同期的な処理
    await Task.Delay(1000);
    // 例外を発生される
    throw new ApplicationException();
}

■ 実行結果

ApplicationExceptionが発生

waitで待ち合わせするとAggregateExceptionが発生

Task#waitメソッドは、指定した Task の処理が完了するまで待機するメソッドである。

今回ハマったのが、 async, awaitで非同期処理を書いていたとしても、Task#waitで処理を待ち合わせすると、それ以降に発生する例外は、すべて AggregateException にラップされたスローされることである。(まぁ当然と言えば当然ですが。。。)

public static void Sample() {
    
    try {
        DoAsyncTask().Wait();  //ここで wait()を呼ぶと、非同期処理で発生する例外は AggregateExceptionになる
    } catch (ApplicationException) {
        Console.WriteLine("ApplicationExceptionが発生");
    } catch (Exception ex) {
        Console.WriteLine("その他の例外が発生 " + ex.GetType().Name);
    }
}

public static async Task DoAsyncTask() {

    // 非同期的な処理
    await Task.Delay(1000);
    // 例外を発生される
    throw new ApplicationException();
}

■ 実行結果

その他の例外が発生 AggregateException

分かってみれば単純な問題ではあるが、元々 async , awaitだけで作っていた処理に、Task#waitなどで待ち合わせ処理を入れるように改修してしまうと、それ以降の try ~ catch を見直す必要がある点が、以外と漏れてしまうので注意が必要だ。

スポンサーリンク

QooQ