我有一个问题,即当使用时何时安全处理聚合异常。似乎自然的位置将在渔获块内部,因为如果口气块永远不会开火,那就意味着没有例外。但是我看到很多代码具有空的捕获块,并在处理任何发现的异常之前(包括在MS网站上(之前检查了存在聚合exception的存在。
。
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() {
// ...
}
似乎自然的位置将在渔获块内部
是的,这会很好。Task.WhenAll
返回一个任务完成后完成的任务。因此,在您的情况下,当您的代码进入catch
块时,masterTask
已经完成,这意味着所有tasks
都已完成。
您发布的代码工作是因为Task.WhenAll
返回仅在所有子任务完成时完成的任务。
为什么代码这样做?为什么没有catch (Exception ex)
?这是因为等待仅引发第一个内部异常。如果您需要访问多个异常,则此代码模式是这样做的好方法。您还可以执行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);
}
}
您当然可以这样做。本质上是同一件事。在特定情况下使用更方便的东西。