【发布时间】:2017-07-11 17:19:19
【问题描述】:
我有多个任务接受取消令牌并相应地调用ThrowIfCancellationRequested。这些任务将使用Task.WhenAll 同时运行。我希望在任何任务引发异常时取消所有任务。我使用Select 和ContinueWith 实现了这一点:
var cts = new CancellationTokenSource();
try
{
var tasks = new Task[] { DoSomethingAsync(cts.Token), ... } // multiple tasks here
.Select(task => task.ContinueWith(task =>
{
if (task.IsFaulted)
{
cts.Cancel();
}
}));
await Task.WhenAll(tasks).ConfigureAwait(false);
}
catch (SpecificException)
{
// Why is this block never reached?
}
我不确定这是否是最好的方法,它似乎有一些问题。似乎异常将在内部被捕获,总是到达WhenAll 之后的代码。我不希望在发生异常时到达WhenAll 之后的代码,我宁愿抛出异常,以便我可以在调用堆栈的另一个级别手动捕获它。实现这一目标的最佳方法是什么?如果可能的话,我希望调用堆栈保持不变。如果发生多个异常,最好只重新抛出第一个异常,不要AggregateException。
在相关说明中,我尝试将取消令牌传递给 ContinueWith,如下所示:task.ContinueWith(lambda, cts.Token)。但是,当任何任务中发生异常时,这最终会抛出 TaskCanceledException 而不是我感兴趣的异常。我想我应该将取消令牌传递给 ContinueWith 因为这将取消 ContinueWith 本身,这我不认为这是我想要的。
【问题讨论】:
-
@Servy 这个问题和“重复”的区别在于这是关于
Task.WhenAll并在任务之外使用try-catch。另一个问题是关于加入多个ContinueWith并明确检查task.Exception。 -
差异不相关。
WhenAll只是要使用ContinueWIth附加它自己的延续,并且在确定它是否应该出现故障时检查Exception值,给您留下完全相同的问题。其中一些是在WhenAll的幕后发生的,这与解释或纠正问题并没有太大的不同。 -
@Servy 好的,现在我知道我正在处理链式延续,我可以看到其他线程如何回答我的问题。但是,如果我没有问这个问题,我不会知道
WhenAll隐含地附加了它自己的延续,另一个线程没有触及这个主题。所以我看不出我的问题是如何重复的。 -
我期待什么?我不知道。我对TPL的理解为零。感谢
async/await,直到现在我才不得不使用ContinueWith。只是因为您的最后评论,我现在明白,向错误任务添加延续会导致任务不会出现错误。 -
那么您没有阅读副本,因为它会告诉您很多。从字面上看,答案就是这样打开的。
标签: c# .net exception async-await task-parallel-library