在连续链中传播异常的正确方法是什么?
t.ContinueWith(t2 =>
{
if(t2.Exception != null)
throw t2.Exception;
/* Other async code. */
})
.ContinueWith(/*...*/);
t.ContinueWith(t2 =>
{
if(t2.IsFaulted)
throw t2.Exception;
/* Other async code. */
})
.ContinueWith(/*...*/);
t.ContinueWith(t2 =>
{
if(t2.Exception != null)
return t2;
/* Other async code. */
})
.ContinueWith(/*...*/);
t.ContinueWith(t2 =>
{
if(t2.IsFaulted)
return t2;
/* Other async code. */
})
.ContinueWith(/*...*/);
t.ContinueWith(t2 =>
{
t2.Wait();
/* Other async code. */
})
.ContinueWith(/*...*/);
t.ContinueWith(t2 =>
{
/* Other async code. */
}, TaskContinuationOptions.NotOnFaulted) // Don't think this one works as expected
.ContinueWith(/*...*/);
答案 0 :(得分:4)
TaskContinuationOptions.OnlyOn...
可能会有问题,因为如果不符合条件,它们会导致取消。在我理解之前,我在编写的代码中遇到了一些微妙的问题。
像这样的链式延续实际上很难做到。 到目前为止,最简单的修复是使用新的.NET 4.5 await
功能。这使您几乎可以忽略您正在编写异步代码的事实。您可以像使用同步等效块一样使用try / catch块。对于.NET 4,可以使用async targeting pack。
如果您使用的是.NET 4.0,最直接的方法是从每个延续中的前导任务访问Task.Result
,或者,如果它没有返回结果,请使用Task.Wait()
作为您在您的示例代码中执行。但是,您最终可能会得到AggregateException
个对象的嵌套树,稍后您需要解析这些对象才能获得“真正的”异常。 (同样,.NET 4.5使这更容易。虽然Task.Result
抛出AggregateException
,Task.GetAwaiter().GetResult()
- 否则等同于抛出基础异常。)
重申这实际上不是一个小问题,您可能会对Eric Lippert关于C#5异步代码here和here中的异常处理的文章感兴趣。
答案 1 :(得分:1)
如果您不希望在发生异常(即日志记录)时特别执行任何操作,并且只希望传播异常,那么在抛出异常时(或者在发生异常时)不要运行延续取消)。
task.ContinueWith(t =>
{
//do stuff
}, TaskContinuationOptions.OnlyOnRanToCompletion);
如果您明确要处理异常的情况(可能要进行日志记录,请将抛出的异常更改为其他类型的异常(可能包含其他信息,或隐藏不应公开的信息),然后您可以使用OnlyOnFaulted
选项添加延续(可能除了正常的案例延续之外)。
答案 2 :(得分:0)
OnlyOnFaulted
选项适用于前一个Task
无法正确执行任务的情况。一种方法是在每个延续任务中重新抛出异常,但那将是糟糕的设计。
如果要像普通对象一样传递异常,则将其视为一个。让Task
返回异常并在延续中使用Task.Result
属性。
答案 3 :(得分:0)
我们现在在openstack.net SDK中使用的方法是CoreTaskExtensions.cs
中的扩展方法。
这些方法有两种形式:
Then
:续集会返回Task
,并会自动调用Unwrap()
。Select
:continuation返回一个对象,不会调用Unwrap()
。此方法仅适用于轻量级延续,因为它始终指定TaskContinuationOptions.ExecuteSynchronously
。这些方法具有以下好处:
AggregateException
的多个层中。)supportsErrors=true
)。Task
的续传,并为您调用Unwrap()
。以下对比显示了我们如何将此更改应用于 CloudAutoScaleProvider.cs ,其最初广泛使用ContinueWith
和Unwrap
:
https://github.com/openstacknetsdk/openstack.net/compare/3ae981e9...299b9f67#diff-3