Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catch unhandled exceptions from async

When an async method that is awaited upon throws an exception, the exception is stored somewhere and throwing it is delayed. In a WinForms or WPF application, it uses SynchronizationContext.Current to post throwing of the exception. However, in e.g. a console application, it throws the exception on a thread pool and it brings down the application.

How can I prevent exceptions thrown from an async method from bringing down the application?

EDIT:

Appearantly the issue I'm describing is because I have void async methods. See comments.

like image 314
Pieter van Ginkel Avatar asked Sep 07 '12 08:09

Pieter van Ginkel


2 Answers

How can I prevent exceptions thrown from an async method from bringing down the application?

Follow these best practices:

  1. All async methods should return Task or Task<T> unless they have to return void (e.g., event handlers).
  2. At some point, you should await all Tasks returned from async methods. The only reason you wouldn't want to do this is if you no longer care about the result of the operation (e.g., after you cancel it).
  3. If you need to catch an exception from an async void event handler, then catch it in the event handler - exactly like you would do if this was synchronous code.

You may find my async / await intro post helpful; I cover several other best practices there as well.

like image 92
Stephen Cleary Avatar answered Oct 18 '22 03:10

Stephen Cleary


When the async method is started, it captures the current synchronization context. A way to solve this issue is to create your own synchronization context which captures the exception.

The point here is that the synchronization context posts the callback to the thread pool, but with a try/catch around it:

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);
        }
    }
}

In the catch above you can put your exception handling logic.

Next, on every thread (SynchronizationContext.Current is [ThreadStatic]) where you want to execute async methods with this mechanism, you must set the current synchronization context:

SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext());

The complete Main example:

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");
    }
}
like image 22
Pieter van Ginkel Avatar answered Oct 18 '22 04:10

Pieter van Ginkel