异常处理异步编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了异常处理异步编程相关的知识,希望对你有一定的参考价值。

我在这里有一些代码。这是真实类的简化版本:

public class Delayer
{
    //it has to be unawaitable
    public async void Execute(Action action)
    {
        await Task.Delay(10).ConfigureAwait(false);
        action.BeginInvoke(null, null); //action.Invoke();
    }
}

我用它:

private static Task TestFoo()
{
    throw new Exception();
}


delayer.Execute(async () =>
{
    //do something else
    await TestFoo().ConfigureAwait(false);
});

我不能通过将Execute方法传递给try / catch来解决这个异常,我也不能通过将action.BeginInvoke(null, null)传递给try / catch来实现。我可以处理它,如果只有我用try / catch包围async lambda时将它传递给Execute方法。

我的问题是:为什么异步lambda用await执行?因为如果它没有用await执行,那么就会吞下异常。

我希望Execute方法吞下从动作抛出的所有异常。有什么想法怎么做?我做错了什么?

加成:

Execute的行为必须像“只是一场火灾和忘记行动”。

答案

编辑

如果您确实需要Fire和Forget方法,那么唯一要做的就是捕获Execute方法中的所有异常。但是如果你希望能够捕获异常而不是在不可等待的BeginInvoke上使用Action,你必须接受一个等待的任务。

public class Delayer
{
    public async Task Execute(Func<Task> action) // or public async void Execute(Func<Task> action) if you insist on it.
    {
        try
        {
            await Task.Delay(10).ConfigureAwait(false);
            await action.Invoke();
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

你可以安全地做

void CallDelayedMethod()
{
    var delayer = new Delayer();

    delayer.Execute(ThrowException);
}

public Task ThrowException()
{
    throw new Exception();  
}

我仍然会返回一个任务并将其留给调用者,以便在不等待它(不管是否发生)的情况下忽略它。

原始答案

您没有通过在类Delayer中使用async void签名来遵循最佳实践。

public async void Execute(Action action)

应该

public async Task Execute(Action action)

所以你可以等待Execute的电话。否则它只是一个火灾和忘记操作,这使得捕捉异常变得困难。通过使其成为awaitbale,您可以:

try
{
    await Delayer.Execute(...);
}
catch(Exception ex)
{
    ....
}

来自the best practices

异步void方法具有不同的错误处理语义。当异步任务或异步任务方法抛出异常时,将捕获该异常并将其放在Task对象上。使用async void方法时,没有Task对象,因此异步void方法抛出的任何异常都将直接在async void方法启动时处于活动状态的SynchronizationContext上引发。

此外,如果要向其传递可等待的操作,则应该执行Execute accept a Task:

public async Task Execute(Func<Task> action)
{
    await Task.Delay(10).ConfigureAwait(false);
    await action.Invoke();
}

以上是关于异常处理异步编程的主要内容,如果未能解决你的问题,请参考以下文章

C#中异步编程多个异常的处理方式

异步编程的优势和难点

基于任务的异步编程模式(TAP)的错误处理

node中使用domain处理异步异常问题

Java异常处理机制

多线程编程学习笔记——async和await