当返回值为void时,从async捕获异常等待



我发现自己使用异步即发即弃方法,使用void作为返回值,但一定要注意异常。

人们似乎一致认为,如果没有对正在执行的Task的引用,那么async await就无法正确处理异常,而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)
        {
            ExceptionThrown?.Invoke(this, e);
        }
    }
    static void Process(int iteration)
    {
        Thread.Sleep(500);
        if(iteration == 5)
            throw new Exception("AUUCH");
    }
    public event EventHandler<Exception> ExceptionThrown;
}

async / await关键字被编译到IL时,它们实际上会生成一个状态机,请在这里阅读。唯一应该使用async void的时间是在事件处理程序上,如这里所解释的。问题是,当状态机构建出来时,它使用TaskTask<T>类作为返回类型,以便管理链中下一个异步操作的下一个状态。然而,当您将该方法定义为void时,它基本上将null返回到状态机,然后一切都不正常。

catch 无法捕获异步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 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);
    }
}