Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

5 лет назад
Открыть в
День 1050. #ЗаметкиНаПолях #AsyncTips Асинхронные потоки и отмена Создание асинхронных потоков Потребление асинхронных потоков Использование LINQ с асинхронными потоками Задача: требуется механизм отмены асинхронных потоков. Решение Не всем асинхронным потокам необходима отмена. Перечисление может быть просто остановлено при достижении условия. Если это единственная разновидность «отмены», реально необходимая в программе, то полноценная отмена не нужна, как показывает следующий пример (см. код метода GetAsyncRange здесь):
await foreach (int result in GetAsyncRange())
{
  Console.WriteLine(result);
  if (result >= 8)
    break;
}

Часто бывает полезно отменять сами асинхронные потоки, передав маркер отмены потоку-источнику. Async метод, возвращающий IAsyncEnumerable<T>, может получать маркер отмены, который должен быть помечен атрибутом EnumeratorCancellation. После этого маркер можно использовать естественным образом:
async IAsyncEnumerable<int> GetAsyncRange(
[EnumeratorCancellation] CancellationToken ct = default)
{
  for (int i = 0; i < 10; i++)
  {
    await Task.Delay(100, ct);
    yield return i;
  }
}

using var cts = new CancellationTokenSource(500);
CancellationToken token = cts.Token;
await foreach (int result in GetAsyncRange(token)) {
  Console.WriteLine(result);
}

В этом примере CancellationToken передается непосредственно методу, возвращающему асинхронный перечислитель. Это самый распространенный вариант использования. Возможны и другие сценарии, когда маркер отмены применяется к перечислителю. Это нетипичный, но важный сценарий использования. По этой причине асинхронные потоки поддерживают метод расширения WithCancellation, который может использоваться для присоединения маркера отмены к конкретной итерации асинхронного потока:
async Task ConsumeSequence(IAsyncEnumerable<int> items)
{
  using var cts = new CancellationTokenSource(500);
  CancellationToken ct = cts.Token;
  await foreach (int item 
    in items.WithCancellation(ct))
  {
    Console.WriteLine(item);
  }
}

await ConsumeSequence(GetAsyncRange());

Примечания: 1. Заметьте, что GetAsyncRange() вызывается без токена отмены. При наличии атрибута EnumeratorCancellation компилятор автоматически обеспечит передачу маркера из WithCancellation параметру метода GetAsyncRange, помеченному атрибутом EnumeratorCancellation. 2. В нынешнем виде отменённое перечисление выбрасывает TaskCanceledException, которое нужно обработать в клиентском коде. 3. Метод расширения WithCancellation не препятствует ConfigureAwait(false). Оба метода расширения могут быть объединены в цепочку: … items.WithCancellation(ct).ConfigureAwait(false))Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 3.