![](/img/trans.png)
[英]What's the difference between awaiting async Task function and calling await inside a void function?
[英]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
引发的异常,而无需更改调用链中的每个函数以使用async
, await
和返回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.