Захват исключений из async-ожидания, когда void - возвращаемое значение

Я использую методы async fire-and-forget, используя void как возвращаемое значение, но НЕ заботьтесь об исключениях.

Кажется, что консенсус исключение не может быть правильно обработано с ожиданием async, если ссылка на контрольную задачу не выполняется, и void должен быть... ну.. избегать..

Что мне не хватает в следующем коде, который, по-видимому, выполняет эту работу:

class Program
{
    static void Main()
    {
        var p = new Processor();
        p.ExceptionThrown += p_ExceptionThrown;
        for (var i = 0; i < 10; i++)
            p.ProcessAsync(i);
        Console.ReadKey();
    }

    static void p_ExceptionThrown(object sender, Exception e)
    {
        Console.WriteLine("Exception caught in Main : " + e);
    }
}

class Processor
{
    public async void ProcessAsync(int iteration)
    {
        try
        {
            await Task.Run(() => Process(iteration));
        }
        catch (Exception e)
        {
            OnException(e);
        }
    }

    public void Process(int iteration)
    {
        Thread.Sleep(500);
        if(iteration == 5)
            throw new Exception("AUUCH");
    }

    public event EventHandler<Exception> ExceptionThrown;

    void OnException(Exception e)
    {
        var handler = ExceptionThrown;
        if (handler != null)
            handler(this, e);
    }
}

Ответ 1

Под обложками ключевые слова async / await фактически генерируют государственную машину, когда они скомпилированы до IL, пожалуйста, прочитайте об этом здесь. Единственный раз, когда вы когда-либо использовали async void, - это обработчик события, как описано здесь. Проблема заключается в том, что при построении конечного автомата он использует классы Task или Task<T> в качестве типа возврата для управления следующим состоянием следующей асинхронной операции в цепочке. Однако, когда вы определяете метод как void, он в основном возвращает null на конечный автомат, а затем все удаляется.

Исключения из async void can not могут быть пойманы с catch

Цитата из вышеперечисленного - из статьи лучших практик, на которую я указал вам раньше. Ниже приведено изменение, так как я проверил его, чтобы убедиться, что он делает.

class Program
{
    static void Main()
    {
        var p = new Processor();
        p.ExceptionThrown += p_ExceptionThrown;
        for (var i = 0; i < 10; i++)
            p.ProcessAsync(i);
        Console.ReadKey();
    }

    static void p_ExceptionThrown(object sender, Exception e)
    {
        Console.WriteLine("Exception caught in Main : " + e);
    }
}

class Processor
{
    public async Task ProcessAsync(int iteration)
    {
        try
        {
            await Task.Run(() => Process(iteration));
        }
        catch (Exception e)
        {
            OnException(e);
        }
    }

    public void Process(int iteration)
    {
        Thread.Sleep(500);
        if(iteration == 5)
            throw new Exception("AUUCH");
    }

    public event EventHandler<Exception> ExceptionThrown;

    void OnException(Exception e)
    {
        var handler = ExceptionThrown;
        if (handler != null)
            handler(this, e);
    }
}