处理C#AggregateException的正确方法

时间:2019-05-12 00:04:06

标签: c# async-await aggregateexception

我有一个问题,在使用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() {
      // ...
    }

2 个答案:

答案 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);
    }
  }

您当然可以做到。本质上是同一回事。使用在特定情况下更方便的方法。