我有一长串调用,最终在另一个程序集中调用异步函数。我希望这个函数同步执行,可能抛出一个异常,我想在调用链上传播。
以下代码段最少复制了此方案:
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
?
在我的实际代码中,这个异步函数是从一个非常深的函数调用链中调用的,所有函数调用都应该同步执行。当它们永远不会被使用(它们也不能被安全地使用)异步时使它们全部异步似乎是一个可怕的想法,如果可能的话,我想避免它。
答案 0 :(得分:3)
根据MSDN,没有。
您主要使用void返回类型来定义需要返回类型的事件处理程序。返回异步的异步方法的调用者无法等待它,也无法捕获该方法抛出的异常。
答案 1 :(得分:3)
此代码将导致崩溃,因为从Exc抛出的异常不会传播回DoWork。
完全没有。来自Exc
的例外情况正在传递给DoWork
,如果try
中有catch
/ DoWork
,则可以将其排除在那里。
您遇到崩溃的原因是DoWork
传播了该异常,而DoWork
是async 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的任何潜在收益都将丢失。