1

我有一个关于使用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() {
      // ...
    }

4

2 回答 2

2

看起来自然的地方会在 catch 块内

是的,那会很好。Task.WhenAll返回所有任务完成后完成的任务。所以在你的情况下,当你的代码进入catch块时,masterTask已经完成了,这意味着所有的tasks都已经完成。

于 2019-05-12T01:00:35.783 回答
1

您发布的代码有效,因为Task.WhenAll返回的任务仅在所有子任务完成后才完成。

为什么代码会这样?为什么没有catch (Exception ex)?这是因为 await 只抛出第一个内部异常。如果您需要访问多个异常,此 Code Pattern 是一个很好的方法。你也可以做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);
    }
  }

你当然可以这样做。本质上是一样的。在特定情况下使用更方便的东西。

于 2019-05-12T08:55:29.050 回答