繁体   English   中英

如何将从异步函数引发的异常传播到正在等待的异步void函数?

[英]How can I propagate an exception thrown from an asynchronous function to an awaiting async void function?

我有一长串的调用,这些调用最终会在另一个程序集中调用异步函数。 我希望此函数同步执行,并且可能会引发异常,该异常要在调用链中传播。

下面的代码片段最小化了这种情况:

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

我有什么办法可以让Main的catch块处理从Exc引发的异常,而无需更改调用链中的每个函数以使用asyncawait和返回Task

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

根据MSDN ,没有。

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

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

一点也不。 来自Exc的异常将传递给DoWork ,如果您在DoWork进行了try / catch ,则可以在其中捕获该异常。

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

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

该操作要么是异步的,要么不是。 由于您正在调用的低级API是异步的,因此最好是代码以异步方式使用它。 尝试将异步代码包装在同步代码中非常容易出错,这是一个可怕的想法。 虽然有时有必要。

因此, 最干净的解决方案是使异步方法具有异步签名。 是的,这意味着要“一直保持异步 ”,正如我在有关异步最佳实践的MSDN文章中所述。 但是,如果您喜欢通过异步进行同步,那么可以选择我在我最近的“ Brownfield async”文章中描述各种黑客之一

如果您坚持不让整个堆栈异步,那么我将提供斯蒂芬斯答案的反驳:

会使DoWork() { Exec().Wait(); } DoWork() { Exec().Wait(); }为您工作?

控制台应用程序中没有异步死锁。 如果这不是控制台应用程序,则可以使用Ivan链接到的应用程序,或者使用ConfigureAwait(false)或使用Task.Run(...).Wait()不会死锁,因为任务主体没有同步上下文。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM