Ответ
Для отмены асинхронных операций в C# используется паттерн Cooperative Cancellation на основе CancellationTokenSource и CancellationToken. Отмена является запросом, а не принудительной остановкой потока.
Базовый паттерн
- Создание источника отмены:
CancellationTokenSource cts = new(); - Получение токена:
CancellationToken token = cts.Token; - Передача токена в асинхронный метод.
- Запрос отмены: Вызов
cts.Cancel()илиcts.CancelAfter(timeout). - Реакция на отмену внутри метода: периодическая проверка
token.ThrowIfCancellationRequested()илиtoken.IsCancellationRequested.
Пример: Отмена длительной операции
public async Task ProcessDataAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 100; i++)
{
// 1. Проверяем запрос на отмену. Если он был, выбрасывается OperationCanceledException.
cancellationToken.ThrowIfCancellationRequested();
// 2. Или проверяем флаг для более гибкой логики (например, graceful shutdown).
if (cancellationToken.IsCancellationRequested)
{
// Выполняем очистку ресурсов...
break;
}
// Полезная работа...
await Task.Delay(100, cancellationToken); // Task.Delay также поддерживает отмену.
}
}
// Использование
async Task MainMethod()
{
using var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(5)); // Автоотмена через 5 секунд.
try
{
await ProcessDataAsync(cts.Token);
Console.WriteLine("Операция завершена успешно.");
}
catch (OperationCanceledException) // Ловим именно этот тип исключения.
{
Console.WriteLine("Операция была отменена.");
}
}
Ключевые моменты
- Распространение токена: Все асинхронные методы в цепочке вызовов должны принимать и передавать
CancellationTokenдальше, если это возможно. - Поддержка в библиотеках: Многие методы в .NET (например,
HttpClient.SendAsync,Stream.ReadAsync,DbContext.SaveChangesAsync) имеют перегрузки с параметромCancellationToken. Всегда используйте их. - Использование
using:CancellationTokenSourceреализуетIDisposable. Всегда оборачивайте его вusingили вызывайтеDispose()для своевременного освобождения ресурсов таймера. - Исключение
TaskCanceledException: Является производным отOperationCanceledException. Некоторые API (например,HttpClient) бросают именно его. Ловите базовыйOperationCanceledException.
Важно: Этот механизм не убивает поток. Он лишь устанавливает флаг, который ваша кодовая логика должна корректно обработать.
Ответ 18+ 🔞
Слушай, про отмену асинхронных операций в C# — это ж классика, как водка под селёдку. Весь этот цирк с CancellationTokenSource и CancellationToken называется Cooperative Cancellation, что в переводе на русский значит «отмена по-хорошему, без мордобоя». То есть мы не прибиваем поток дубиной, а вежливо просим: «Слышь, братан, может, уже хватит?»
Суть, если по-простому
- Заводишь источник отмены:
CancellationTokenSource cts = new();— это как дистанционка от телевизора, только для операции. - Выдёргиваешь из него токен:
CancellationToken token = cts.Token;— это уже сама кнопка, которую ты суёшь в метод. - Метод работает и периодически смотрит на эту кнопку: «Меня уже не отменили?»
- Когда надо остановиться — жмёшь
cts.Cancel(): Или ставишь таймер черезCancelAfter, чтобы само отрубилось, как духовка. - Метод видит, что кнопку нажали, и начинает аккуратно сворачиваться, а не бросает всё на полпути.
Ну, пример, чтобы совсем понятно было
Вот смотри, есть у нас метод, который делает вид, что работает:
public async Task ProcessDataAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 100; i++)
{
// Способ 1: Жёсткий. Если отмена запрошена — сразу вылетает исключение.
cancellationToken.ThrowIfCancellationRequested();
// Способ 2: Мягкий. Можно проверить флаг и сделать что-то умное перед выходом.
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Ладно, ладно, уже выхожу...");
// Тут по-хорошему закрываешь файлы, откатываешь транзакции, ну ты понял.
break;
}
// Полезная работа... (на самом деле просто спим)
await Task.Delay(100, cancellationToken); // Кстати, Delay тоже токен понимает!
}
}
// А вот как этим пользоваться
async Task MainMethod()
{
using var cts = new CancellationTokenSource(); // using — чтобы не было утечек, это важно!
cts.CancelAfter(TimeSpan.FromSeconds(5)); // Автоматически отменится через 5 секунд, даже если ты уснёшь.
try
{
await ProcessDataAsync(cts.Token);
Console.WriteLine("Всё сделал, начальник!");
}
catch (OperationCanceledException) // Ловишь именно это исключение, оно тут главное.
{
Console.WriteLine("Операцию отменили, да похуй.");
}
}
Главные фишки, которые надо запомнить
- Токен — как эстафетная палочка. Ты получил его сверху — передавай дальше, во все методы, которые его поддерживают. Не жадничай.
- Фреймворк — не дурак. Куча стандартных методов (
HttpClient.SendAsync, чтение файлов, работа с БД) уже имеют перегрузки сCancellationToken. Всегда, блядь, используй их! Это чтобы если пользователь закрыл окно, твой запрос не висел до скончания времён. CancellationTokenSource— одноразовый шприц. ПослеCancel()его использовать нельзя. Создавай новый. И оборачивай вusing, он таймеры внутри держит, чтобы ресурсы не текли.- Исключения.
ThrowIfCancellationRequested()кидаетOperationCanceledException. Некоторые либы (тот жеHttpClient) могут кидатьTaskCanceledException(он от него наследуется). Так что лови обычноOperationCanceledException, и всё будет ок.
И самое главное: этот механизм — не волшебная палочка. Он не вырубает поток нахуй. Он просто ставит флажок. А уж твой код должен быть не мудаком и этот флажок вовремя заметить, иначе будет работать, пока конь не валится.