TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #2242 2.6K
День 1856. #ЗаметкиНаПолях
Получаем Все Исключения из 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
  • 👍 25
More from @netdeveloperdiary
  1. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
  2. Oct 7, 2026День 2807. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Окончание Нач…
  3. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  4. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  5. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  6. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →