Получаем Все Исключения из Parallel.ForEachAsync
На C# вы можете написать асинхронный код, похожий на синхронный код. Люди, использующие async/await, ожидают, что смогут перехватывать исключения, как в синхронном коде. Это очень удобно, но иногда вам нужно получить все исключения из асинхронного метода.
try
{
// Это выбросит несколько исключений
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
throw new Exception(i.ToString());
});
}
// Обработает только первое исключение
// Остальные потеряются
catch (Exception ex)
{
// Выведет случайное число от 1 до 10,
// в зависимости от порядка обработки
Console.WriteLine(ex.ToString());
}
Если вы хотите получить все исключения, необходимо использовать Task.Exceptions или метод, предоставляющий содержимое этого метода, например Task.Wait.
try
{
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
throw new Exception(i.ToString());
}).WithAggregateException();
}
catch (Exception ex)
{
// Выведет все исключения,
// т.к. ex - это AggregateException
Console.WriteLine(ex.ToString());
}
internal static class TaskExtensions
{
public static async Task
WithAggregateException(this Task task)
{
// Отключаем выброс исключений,
// Они будут обработаны в task.Wait()
await task.ConfigureAwait(
ConfigureAwaitOptions.SuppressThrowing);
// Задача уже выполнена, поэтому Wait только
// выбросит AggregateException при ошибке
task.Wait();
}
}
Заметьте, что Parallel.ForEachAsync прекращает обработку элементов из коллекции после выброса первого исключения. Он ожидает завершения текущих задач, а затем сообщает об исключениях. Количество выполненных задач будет зависеть от количества параллельно выполняемых операций. Например, если задать для Parallel.ForEachAsync опцию
var options = new ParallelOptions() {
MaxDegreeOfParallelism = 3
};то будут выполнены только 3 задачи.
Чтобы этого избежать, можно внутри задачи сохранять выброшенные исключения:
var exceptions = new ConcurrentBag<Exception>();
await Parallel.ForEachAsync(
Enumerable.Range(1, 10),
async (i, _) =>
{
try
{
throw new Exception(i.ToString());
}
catch (Exception ex)
{
exceptions.Add(ex);
}
});
if (!exceptions.IsEmpty)
throw new AggregateException(exceptions);
Источник: https://www.meziantou.net/getting-all-exceptions-thrown-from-parallel-foreachasync.htm