Правильный способ обработки C # AggregateException - PullRequest
1 голос
/ 12 мая 2019

У меня есть вопрос о том, когда безопасно обрабатывать статистические исключения при использовании WhenAll (). Кажется, что естественное место будет внутри блока catch, потому что, если блок catch никогда не сработает, это будет означать, что нет никаких исключений для обработки. Но я вижу много кода, который имеет пустой блок catch и проверяет существование AggregateException перед обработкой любых найденных исключений (в том числе на веб-сайте MS).


    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 ]

2 голосов
/ 12 мая 2019

Кажется, что естественное место будет внутри блока захвата

Да, это будет работать нормально.Task.WhenAll возвращает задание, которое завершается после завершения всех заданий.Таким образом, в вашем случае к тому времени, когда ваш код входит в блок catch, masterTask завершен, и это означает, что все tasks завершены.

1 голос
/ 12 мая 2019

Код, который вы опубликовали, работает, потому что 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);
    }
  }

Вы, безусловно, можете это сделать.По сути это одно и то же.Используйте то, что удобнее в конкретной ситуации.

...