如何将从异步函数抛出的异常传播到等待异步void函数?

时间:2015-12-15 20:22:05

标签: c# asynchronous async-await

我有一长串调用,最终在另一个程序集中调用异步函数。我希望这个函数同步执行,可能抛出一个异常,我想在调用链上传播。

以下代码段最少复制了此方案:

static Task<int> Exc()
{
    throw new ArgumentException("Exc");
    return Task.FromResult(1);
}

static async void DoWork()
{
    await Exc();
}

static void Main(string[] args)
{
    try
    {
        DoWork();
    }
    catch (Exception e)
    {
        Console.WriteLine("Caught {0}", e.Message);
    }
}

此代码会导致崩溃,因为Exc引发的异常不会传播回Main

我有没有办法让Exc中的catch块处理Main抛出的异常,而不会更改我的调用链中的每个函数以使用async,{ {1}},并返回await

在我的实际代码中,这个异步函数是从一个非常深的函数调用链中调用的,所有函数调用都应该同步执行。当它们永远不会被使用(它们也不能被安全地使用)异步时使它们全部异步似乎是一个可怕的想法,如果可能的话,我想避免它。

3 个答案:

答案 0 :(得分:3)

根据MSDN,没有。

  

您主要使用void返回类型来定义需要返回类型的事件处理程序。返回异步的异步方法的调用者无法等待它,也无法捕获该方法抛出的异常。

答案 1 :(得分:3)

  

此代码将导致崩溃,因为从Exc抛出的异常不会传播回DoWork。

完全没有。来自Exc的例外情况正在传递给DoWork,如果try中有catch / DoWork,则可以将其排除在那里。

您遇到崩溃的原因是DoWork传播了该异常,而DoWorkasync void方法。通过DoWork async Task方法可以轻松避免这种情况(请注意,async void 应用于事件处理程序,DoWork是显然不是)。正如我在MSDN关于最佳实践的文章中所描述的那样,努力avoid async void

  

在我的实际代码中,这个异步函数是从一个非常深的函数调用链中调用的,所有函数调用都应该同步执行。当它们永远不会被使用(它们也不能被安全地使用)异步时使它们全部异步似乎是一个可怕的想法,如果可能的话我想避免它。

操作是异步的,也可能不是。由于您调用的低级API是异步的,因此最好是您的代码异步使用它。尝试将异步代码包装在同步代码中非常容易出错,这是一个糟糕的主意。虽然,有时候,这是必要的。

因此,最干净的解决方案是使异步方法具有异步签名。是的,这意味着要进行“async all the way”,正如我在MSDN上关于异步最佳实践的文章中所描述的那样。但是,如果您希望执行同步异步,则可以选择variety of hacks that I describe in my recent "Brownfield async" article之一。

答案 2 :(得分:0)

如果您坚持使整个堆栈异步,我将提供Stephens答案的反击点:

无效DoWork() { Exec().Wait(); }会无效吗?

控制台应用中没有异步死锁。如果这不是控制台应用,您可以使用what Ivan linked to,或使用ConfigureAwait(false),或使用不会死锁的Task.Run(...).Wait(),因为任务正文没有同步上下文。

请注意,通过执行任何此操作,异步IO的任何潜在收益都将丢失。