День 2303. #ЗаметкиНаПолях
AsyncEnumerable в C#: Важность Атрибута EnumeratorCancellation
Сегодня рассмотрим IAsyncEnumerable<T> и почему атрибут EnumeratorCancellation для параметра CancellationToken имеет решающее значение для написания надёжного отменяемого асинхронного кода.
IAsyncEnumerable<T> — это асинхронный аналог IEnumerable<T> (см. подробнее про создание и потребление асинхронных потоков).
IAsyncEnumerable<T> позволяет:
- Выполнять асинхронные операции во время итерации последовательности;
- Выдавать результаты по мере их появления;
- Обрабатывать отмену.
Необходимость отмены
Поддержка отмены в асинхронных операциях нужна в следующих сценариях:
- Пользователь уходит с веб-страницы, которая извлекает данные;
- Операция поиска занимает слишком много времени, и пользователь отменяет её;
- Сервис завершает работу во время выполнения операций.
Без надлежащей поддержки отмены ресурсы могут быть потрачены впустую, а приложения могут перестать отвечать или произойдёт утечка ресурсов.
Атрибут EnumeratorCancellation
При применении к параметру CancellationToken в методе асинхронного итератора этот атрибут сообщает компилятору о необходимости передать этот токен методу GetAsyncEnumerator, когда итератор используется через await foreach. Вот простой пример:
public async IAsyncEnumerable<int>
GenerateNumbersAsync(
int count,
[EnumeratorCancellation]
CancellationToken ct = default)
{
for (int i = 0; i < count; i++)
{
// Проверяем отмену
ct.ThrowIfCancellationRequested();
// Симулируем асинхронную работу
await Task.Delay(100, ct);
yield return i;
}
}
Использование метода через вызов WithCancellation:
CancellationTokenSource cts = new();
await foreach (var item in
GenerateNumbersAsync(10)
.WithCancellation(cts.Token))
{
Console.WriteLine(item);
}
Без атрибута EnumeratorCancellation существует разрыв между токеном отмены, переданным в метод асинхронного итератора, и токеном, переданным в GetAsyncEnumerator при потреблении перечисляемого объекта. Атрибут EnumeratorCancellation гарантирует, что этот токен попадёт в метод асинхронного итератора, позволяя вам реагировать на запросы отмены от потребителя.
Чтобы помочь вам избежать этой ошибки, компилятор выдает предупреждение CS8425 в качестве напоминания о правильной реализации отмены в ваших методах асинхронного итератора:
CS8425: Async-iterator method 'MyMethod' has parameters that are declared with 'CancellationToken' but are not decorated with the 'EnumeratorCancellation' attribute, and will not be passed the cancellation token from 'WithCancellation' method calls
CS8425: Метод асинхронного итератора 'MyMethod' имеет параметры, объявленные с 'CancellationToken', но не декорированные атрибутом 'EnumeratorCancellation', и ему не будет передан токен отмены из вызовов метода 'WithCancellation'.
Предупреждение возникает, когда вы определяете метод асинхронного итератора, который возвращает IAsyncEnumerable<T> и включает параметр CancellationToken, но вы не декорировали этот параметр атрибутом [EnumeratorCancellation].
Игнорирование этого предупреждения может привести к неявным ошибкам, когда отмена, кажется, реализована, но на самом деле не работает. Код будет компилироваться и запускаться, но запросы на отмену от потребителей будут игнорироваться.
Итого
При работе с асинхронными потоками:
- Используйте атрибут EnumeratorCancellation для параметров токена отмены;
- Регулярно проверяйте отмену;
- Распространяйте токен на внутренние операции;
- Обратите внимание на предупреждение компилятора CS8425, поскольку оно указывает на потенциальную проблему отмены.
Источник: https://bartwullems.blogspot.com/2025/04/asyncenumerable-in-c-importance-of.html
>>Click here to continue<<