我有一个问题,在使用WhenAll()时何时可以安全地处理聚合异常。似乎自然而然地将其放置在catch块内,因为如果catch块从不触发,则意味着没有异常要处理。但是我看到很多代码都有一个空的catch块,并在处理任何发现的异常之前(包括在MS网站上)检查AggregateException的存在。
public async Task MyMethod() {
var tasks = new List<Task>();
for (var i = 0; i < 10; i++) {
tasks.Add(DoSthAsync());
}
var masterTask = Task.WhenAll(tasks);
try {
var results = await masterTask;
} catch {
// Safe to access masterTask here and handle aggregate exceptions? Have all tasks completed?
foreach (var ex in masterTask.Exception.innerExceptions) {
HandleException(ex);
}
}
// Or necessary to check for and handle aggregate exceptions here?
if (masterTask.Exception != null) {
foreach (var ex in masterTask.Exception.innerExceptions) {
HandleException(ex);
}
}
}
public async Task DoSthAsync() {
// ...
}
答案 0 :(得分:2)
自然的地方似乎就在捕获区中
是的,那很好。 Task.WhenAll
返回一个任务,该任务在所有任务均已完成时完成。因此,在您的情况下,当您的代码进入catch
块时,masterTask
已完成,这意味着所有tasks
已完成。
答案 1 :(得分:1)
您发布的代码之所以有效,是因为Task.WhenAll
返回的任务只有在所有子任务都完成后才能完成。
为什么代码这样做呢?为什么不catch (Exception ex)
?这是因为await仅引发第一个内部异常。如果您需要访问多个异常,则此代码模式是一种很好的方法。您还可以执行catch (AggregateException ex)
并使用该对象。是同一对象。
我个人避免这样使用catch
。本质上,它在控制流中使用异常。这使调试更加困难,并可能导致冗长的代码。
我喜欢这样:
var whenAllTask = Task.WhenAll(...);
await whenAllTask.ContinueWith(_ => { }); //never throws
if (whenAllTask.Exception != null) ... //handle exceptions
我将.ContinueWith(_ => { })
位做成了WhenCompleted
扩展方法,以使代码看起来干净。
然后,您想知道检查异常的第二种方法是否是一个好主意:
// Or necessary to check for and handle aggregate exceptions here?
if (masterTask.Exception != null) {
foreach (var ex in masterTask.Exception.innerExceptions) {
HandleException(ex);
}
}
您当然可以做到。本质上是同一回事。使用在特定情况下更方便的方法。