.NET 6: Улучшения Потоковой Обработки. Начало
Продолжаем знакомиться с новинками .NET 6.
Несмотря на то, что существует множество библиотек, позволяющих абстрагироваться от сложностей асинхронного и параллельного программирования, разработчикам всё же необходимо время от времени использовать низкоуровневую логику обработки потоков.
1. Асинхронный Parallel.ForEach
Когда был создан класс Parallel, в C# не было языковой поддержки асинхронного программирования. Хотя в .NET 1.1 был шаблон
IAsyncResult, он не получил широкого распространения, и подавляющее большинство кода было разработано для синхронного выполнения.Это стало проблемой, поскольку акцент сместился на асинхронный код с использованием async/await. В настоящее время нет встроенной поддержки для запуска операции
Parallel.ForEach и асинхронного ожидания результата. Однако программисты всё время нуждаются в этом. Они используют очень плохие обходные пути, такие как одновременный запуск всех элементов параллельно с последующим вызовом
WhenAll. То есть запускают 10000 HTTP-вызовов и задаются вопросом, почему это так плохо работает. Либо приходится делить задачи на пакеты. Это также не столь эффективно, т.к. задачи в пакете выполняются синхронно и степень параллелизма уменьшается. Или же приходится писать циклы с коллекциями задач и странными схемами ожидания.Чтобы решить эту проблему, был создан набор методов
Parallel.ForEachAsync. Каждый метод принимает IEnumerable или IAsyncEnumerable. Опционально могут быть переданы экземпляр ParallelOptions или токен отмены:public static Task ForEachAsync<TSource>(
IEnumerable<TSource> source,
ParallelOptions parallelOptions,
Func<TSource, CancellationToken, ValueTask> body);
public static Task ForEachAsync<TSource>(
IAsyncEnumerable<TSource> source,
CancellationToken cancellationToken,
Func<TSource, CancellationToken, ValueTask> body);
Parallel.ForEachAsync - один из редких случаев, когда вместо Task используется ValueTask. Основная идея здесь в том, что поскольку значения будут обрабатываться в цикле, дополнительные накладные расходы на создание полного объекта Task неоправданы.Хотя в целом эта функция была хорошо принята, одним из спорных моментов было то, следует ли требовать от разработчиков предоставления значения степени параллелизма. Оно означало бы примерное количество потоков, которые будут назначены для выполнения параллельной операции.
Было решено, что, поскольку большинство разработчиков не знают идеальной степени параллелизма для своей рабочей нагрузки, то и ожидать от них передачи этого значения было бы контрпродуктивно. По умолчанию выбрано
Environment.ProcessorCount.Окончание следует…
Источник: https://www.infoq.com/news/2021/08/net6-Threading/