如何让异常从异步方法向上传播到堆栈?(不能使用async或wait关键字)

本文关键字:不能 async 关键字 wait 堆栈 异常 异步方法 传播 | 更新日期: 2023-09-27 18:25:30

请注意这个简单的代码:

   try
   {
     var t = Task.Factory.StartNew<bool>(() => { throw new Exception("aaa"); });
     t.ContinueWith(_ => {}, TaskContinuationOptions.OnlyOnRanToCompletion).Wait();
   }
   catch (Exception exc)
   {
     Debug.WriteLine(exc);
   }

我假设,如果t有一个与之关联的异常,那么Wait()将按原样重新抛出此异常。然而,成功的存在只是延续,似乎改变了这种行为。相反,抛出的是一个"任务已取消"异常。

事实上,在Wait()之前链接TaskContinuationOptions.NotOnRanToCompletion完成处理程序表明,传递给它的任务没有出现故障,而是被取消了:

t.ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnRanToCompletion)
 .ContinueWith(t2 => Debug.Assert(t2.IsCanceled), TaskContinuationOptions.NotOnRanToCompletion)
 .Wait();

这一切都有点奇怪。这意味着,我不能只是将我的快乐路径完成处理程序链接起来,让任何异常传播到与等待线程的最终会合。

我在这里错过了什么?

注意

我仅限于.NET 4.0,因此没有awaitasync关键字。

如何让异常从异步方法向上传播到堆栈?(不能使用async或wait关键字)

我在这里错过了什么?

您缺少.NET 4.5。如果没有awaitasync关键字,任务仍然很有用,但如果你想要你所说的"快乐之路"行为,你需要升级(请参阅下面的更新)。

因为任务比标准的贯穿代码更复杂,而且它们可以通过各种方式连接在一起,所以您需要直接从Task请求异常,而不是调用.Wait()引发的异常。

var t = Task.Factory.StartNew<bool>(() => { throw new Exception("aaa"); });
   try
   {
     t.ContinueWith(_ => {}, TaskContinuationOptions.OnlyOnRanToCompletion)
        .Wait();
   }
   catch (AggregateException exc)
   {
     Debug.WriteLine(exc.InnerExceptions[0]);// "A task was canceled"
     Debug.WriteLine(t.Exception.InnerExceptions[0]);// "aaa"
   }

更新:如果您使用的是Visual Studio 2012,则可以使用asyncawait关键字,而无需升级到4.5。感谢@zespri指出这一点。

更新2:如果您想在顶级捕获并记录适当的异常,只需养成将.Wait()方法封装在try/catch块中的习惯,即可封装给定的异常。

try
{
  t.Wait();
}
catch (AggregateException exc)
{
  throw new Exception("Task Foo failed to complete", t.Exception);
}