TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.77K subscribers
Post #1286 1.64K
День 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.
More from @netdeveloperdiary
  1. Oct 15, 2026Post #3374
  2. Oct 14, 2026Post #3373
  3. Oct 13, 2026День 2813. #ЗаметкиНаПолях #Async Ещё Раз Про Async/Await в C#. Часть 1 Почему существует…
  4. Oct 12, 2026День 2812. #Карьера #AI Как Разработчикам Оставаться Полезными, Когда Код Пишет ИИ? По мер…
  5. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  6. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
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 →