День 1030. #ЗаметкиНаПолях #AsyncTips
Потребление асинхронных потоков
Создание асинхронных потоков
Задача: обработать результаты асинхронного потока.
Решение
Потребление асинхронного потока основано на объединении конструкций ожидания и перечисления в await foreach
. Например, для асинхронного потока, который потребляет ответы API по страницам, можно организовать вывод элементов в консоль:
public async Task ProcessAsync(HttpClient client)
{
await foreach (var value in GetAsync(client))
{
Console.WriteLine(value);
}
}
На концептуальном уровне вызывается метод
GetAsync
, который возвращает IAsyncEnumerable<T>
. Цикл foreach затем создаёт асинхронный перечислитель на базе асинхронного потока. Асинхронные перечислители на логическом уровне похожи на обычные перечислители, не считая того, что операция «получить следующий элемент» может быть асинхронной. Таким образом, await foreach
будет ожидать поступления следующего элемента или завершения асинхронного перечислителя. Если элемент поступил, то await foreach
выполнит тело цикла; если асинхронный перечислитель завершён, происходит выход из цикла.Также можно выполнить асинхронную обработку каждого элемента:
await foreach (var val in GetAsync(client))
{
// асинхронная работа
await Task.Delay(100);
Console.WriteLine(val);
}
В этом случае
await foreach
не переходит к следующему элементу до завершения тела цикла. Таким образом, await foreach
асинхронно получит первый элемент, после чего асинхронно выполнит тело цикла для первого элемента, затем асинхронно получит второй элемент, асинхронно выполнит тело цикла для второго элемента и т.д.В
await foreach
скрыта команда await: к операции «получить следующий элемент» применяется await
. С обычной командой await
можно обойти захват контекста с помощью ConfigureAwait(false)
. Асинхронные потоки также это поддерживают:await foreach (var val in
GetValuesAsync(client).ConfigureAwait(false))
{
await Task.Delay(100)
.ConfigureAwait(false);
Console.WriteLine(val);
}
Итого
await foreach
— самый логичный способ потребления асинхронных потоков. Язык поддерживает ConfigureAwait(false)
для предотвращения захвата контекста в await foreach
. Также возможен вариант с передачей маркеров отмены. Об этом позже.Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 3.
>>Click here to continue<<