c# - 在 ContinueWith 中重新抛出先前的异常

标签 c# exception task-parallel-library

###简介

在对我的代码进行了一段时间的研究后,我发现异常不一定通过ContinueWith传播:

int zeroOrOne = 1;
Task.Factory.StartNew(() => 3 / zeroOrOne)
    .ContinueWith(t => t.Result * 2)
    .ContinueWith(t => Console.WriteLine(t.Result))
    .ContinueWith(_ => SetBusy(false))
    .LogExceptions();

在此示例中,SetBusy 行“重置”了异常链,因此看不到除以零的异常,随后在我面前炸毁了 “A Task's exception (s) 没有被观察到……”

所以...我自己写了一些扩展方法(有很多不同的重载,但基本上都是这样做的):

public static Task ContinueWithEx(this Task task, Action<Task> continuation)
{
     return task.ContinueWith(t =>
     {
         if(t.IsFaulted) throw t.Exception;
         continuation(t);
     });
}

再四处搜索,我发现了 this博客文章,他在其中提出了类似的解决方案,但使用的是 TaskCompletionSource,其(解释)如下所示:

public static Task ContinueWithEx(this Task task, Action<Task> continuation)
{
     var tcs = new TaskCompletionSource<object>();
     task.ContinueWith(t =>
     {
         if(t.IsFaulted) tcs.TrySetException(t.Exception);
         continuation(t);
         tcs.TrySetResult(default(object));
     });
     return tcs.Task;
}

###问题 这两个版本是否严格等效?还是 throw t.Exceptiontcs.TrySetException(t.Exception) 之间存在细微差别?

此外,整个互联网上显然只有一个人这样做的事实是否表明我错过了这样做的惯用方式?

最佳答案

两者之间的区别很微妙。在第一个示例中,您抛出了任务返回的异常。这将触发 CLR 中的正常异常抛出和捕获,ContinueWith 将捕获并包装它并将其传递给链中的下一个任务。

在第二个过程中,您正在调用 TrySetException,它仍然会包装异常并将其传递给链中的下一个任务,但不会触发任何 try/catch 逻辑。

ContinueWithEx 之后的最终结果是 AggregateException(AggregateException(DivideByZeroException))。我看到的唯一区别是内部 AggregateException 在第一个示例中设置了堆栈跟踪(因为它被抛出),而在第二个示例中没有堆栈跟踪。

两者都不太可能比另一个快得多,但我个人更喜欢后者以避免不必要的抛出。

我曾做过这样的事情,其中​​延续返回了一个结果。我将其称为 Select,处理上一个任务被取消的情况,提供重载以修改异常而不是结果或除结果之外,并使用 ExecuteSynchronously 选项。当延续本身会返回一个任务时,我根据 this article 中的代码调用了 Then

关于c# - 在 ContinueWith 中重新抛出先前的异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11839959/

相关文章:

c# - 派生类型的 GetFields

java - Spring 异常

Swift:解析数字字符串时如何捕获异常?

exception - Kotlin/Android – 带有 lambda 的数据类中的 KotlinReflectionInternalError

c# - 检查当前用户是否为管理员

c# - 从弹出窗口返回一个对象

c# - OWIN 能否替代 ASP.NET MVC 应用程序中的 DI?

c# - 如何使用异步方法正确编写 Parallel.For

c# - 使用 Task.Unwrap 获取内部任务

.net - 当 .NET 4.5 中任务异步中的任何方法永远不会完成时