1

请注意这个简单的代码:

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

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

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

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

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

我在这里想念什么?

笔记

我仅限于 .NET 4.0,所以没有awaitasync关键字。

4

1 回答 1

2

我在这里想念什么?

您缺少 .NET 4.5。await没有and关键字的任务仍然有用async,但如果你想要你所说的“快乐路径”行为,你需要升级(见下面的更新)。

因为任务比标准的贯穿代码更复杂,而且它们可以通过多种方式连接在一起,所以您需要直接从 请求异常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,看来您可以使用asyncandawait关键字而无需升级到 4.5。感谢@zespri 指出这一点。

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

try
{
  t.Wait();
}
catch (AggregateException exc)
{
  throw new Exception("Task Foo failed to complete", t.Exception);
}
于 2013-07-30T21:59:41.017 回答