День 1043. #ЗаметкиНаПолях #AsyncTips
Использование LINQ с асинхронными потоками
Создание асинхронных потоков
Потребление асинхронных потоков
Задача: обработать асинхронный поток с использованием чётко определенных и хорошо протестированных операторов. Либо использовать LINQ, когда предикат является асинхронным, например, необходимо провести поиск каждого элемента в базе данных или API, чтобы узнать, должен ли он быть включен в итоговую последовательность.
Решение
Допустим, у нас есть метод, производящий асинхронную последовательность:
async IAsyncEnumerable<int> GetAsyncRange()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(100);
yield return i;
}
}
IAsyncEnumerable<T>
включает поддержку LINQ в NuGet-пакете System.Linq.Async. Заметьте, что при этом в коде нужно использовать using static System.Linq.AsyncEnumerable;Операторы LINQ можно применять к асинхронным потокам. Доступны все знакомые операторы:
Where
, Select
, SelectMany
, Join
и т.д. Результат представляет собой асинхронный поток:IAsyncEnumerable<int> values =Но что, если нам нужно передать в
GetAsyncRange().Where(value => value % 2 == 0);
await foreach (int result in values) {
Console.WriteLine(result);
}
Where
асинхронный предикат? Where
их не поддерживает. Но есть подходящий оператор WhereAwait
:IAsyncEnumerable<int> values =Методы LINQ для асинхронных потоков можно использовать и для обычных перечисляемых объектов, вызвав
GetAsyncRange().WhereAwait(
async value => {
// Асинхронная работа для определения,
// должен ли элемент быть включён в результат
await Task.Delay(10);
return value % 2 == 0;
});
ToAsyncEnumerable()
для любого IEnumerable<T>
. Результат можно использовать с WhereAwait
, SelectAwait
и другими операторами, принимающими асинхронные делегаты.Операторы LINQ для асинхронных потоков могут оканчиваться суффиксами
Async
и Await
. Операторы, заканчивающиеся суффиксом Async
, возвращают обычное значение, допускающее ожидание. Операторы с суффиксом Await
получают асинхронный делегат. Т.е. Await
в имени подразумевает, что они используют await
внутри переданного делегата.Пример
WhereAwait
был приведёт выше.Суффикс
Async
применяется только к операторам терминации (termination operators), которые извлекают некоторое значение или выполняют некоторые вычисления и возвращают асинхронное скалярное значение. Пример такого оператора — CountAsync
, версия Count
для асинхронного потока, которая может подсчитать количество элементов, соответствующих некоторому предикату:int count = await GetAsyncRange()Предикат может также быть асинхронным. В этом случае используется метод
.CountAsync(value => value % 2 == 0);
CountAwaitAsync
, поскольку он получает асинхронного делегата и производит одно терминальное значение:int count = await GetAsyncRange().CountAwaitAsync(Итого
async value => {
await Task.Delay(10);
return value % 2 == 0;
});
- Операторы без суффиксов (
Where
, Select
) получают синхронные делегаты и выдают асинхронную последовательность.- Операторы, которые получают асинхронные делегаты, имеют суффикс
Await
(WhereAwait
, SelectAwait
), и выдают асинхронную последовательность.- Операторы, возвращающие терминальное значение, имеют суффикс
Async
(CountAsync
), а также могут иметь суффикс Await
, если получают асинхронный делегат (CountAwaitAsync
).Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 3.
>>Click here to continue<<