web-dev-qa-db-ja.com

非同期から未処理の例外をキャッチ

待機中のasyncメソッドが例外をスローすると、例外はどこかに格納され、スローが遅延します。 WinFormsまたはWPFアプリケーションでは、SynchronizationContext.Currentを使用して例外のスローをポストします。ただし、たとえばコンソールアプリケーションの場合、スレッドプールで例外がスローされ、アプリケーションが停止します。

asyncメソッドからスローされた例外がアプリケーションを停止させないようにするにはどうすればよいですか?

編集:

どうやら私が説明している問題は、私がvoidasyncメソッドを持っているためです。コメントを参照してください。

21

asyncメソッドが開始されると、現在の同期コンテキストがキャプチャされます。この問題を解決する方法は、例外をキャプチャする独自の同期コンテキストを作成することです。

ここでのポイントは、同期コンテキストがコールバックをスレッドプールに投稿しますが、その周りにtry/catchがあります。

public class AsyncSynchronizationContext : SynchronizationContext
{
    public override void Send(SendOrPostCallback d, object state)
    {
        try
        {
            d(state);
        }
        catch (Exception ex)
        {
            // Put your exception handling logic here.

            Console.WriteLine(ex.Message);
        }
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        try
        {
            d(state);
        }
        catch (Exception ex)
        {
            // Put your exception handling logic here.

            Console.WriteLine(ex.Message);
        }
    }
}

上記のcatchに、例外処理ロジックを配置できます。

次に、すべてのスレッドで(SynchronizationContext.Current[ThreadStatic])このメカニズムでasyncメソッドを実行する場合は、現在の同期コンテキストを設定する必要があります。

SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext());

完全なMainの例:

class Program
{
    static void Main(string[] args)
    {
        SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext());

        ExecuteAsyncMethod();

        Console.ReadKey();
    }

    private static async void ExecuteAsyncMethod()
    {
        await AsyncMethod();
    }

    private static async Task AsyncMethod()
    {
        throw new Exception("Exception from async");
    }
}
11

非同期メソッドからスローされた例外がアプリケーションをダウンさせるのを防ぐにはどうすればよいですか?

次のベストプラクティスに従ってください。

  1. すべてのasyncメソッドは、haveTaskを返す場合を除いて、voidまたはTask<T>を返す必要があります(イベントハンドラーなど)。
  2. ある時点で、awaitメソッドから返されたすべてのTasksをasyncする必要があります。これを実行したくない唯一の理由は、操作の結果を気にしなくなった場合(たとえば、操作をキャンセルした後)です。
  3. async voidイベントハンドラーから例外をキャッチする必要がある場合は、イベントハンドラーでキャッチします。これが同期コードの場合とまったく同じです。

私の async/await intro post 役立つと思うかもしれません。他のいくつかのベストプラクティスについても説明します。

20
Stephen Cleary