Что такое CancellationToken и для чего он нужен?

«Что такое CancellationToken и для чего он нужен?» — вопрос из категории Многопоточность, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

CancellationToken — это структура в .NET, предназначенная для безопасной и кооперативной отмены долго выполняющихся или асинхронных операций. Он позволяет передать в метод сигнал о запросе на отмену, а методу — периодически проверять этот сигнал и корректно завершать работу.

Основные компоненты:

  1. CancellationTokenSource: Источник токена. Управляет состоянием отмены через метод Cancel().
  2. CancellationToken: Легковесная структура, которая только отражает состояние отмены. Передается в отменяемые методы.

Базовый пример использования:

public async Task ProcessDataAsync(CancellationToken cancellationToken)
{
    // 1. Периодическая ручная проверка
    for (int i = 0; i < 100; i++)
    {
        cancellationToken.ThrowIfCancellationRequested(); // Выбросит OperationCanceledException
        // ... выполнение работы на шаге i ...
        await Task.Delay(100, cancellationToken); // 2. Автоматическая проверка в поддерживающих API
    }
}

// Использование
public async Task MainMethod()
{
    using var cts = new CancellationTokenSource();
    // Запланировать отмену через 5 секунд, например, по таймауту
    cts.CancelAfter(TimeSpan.FromSeconds(5));

    try
    {
        await ProcessDataAsync(cts.Token);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Операция была отменена.");
    }
}

Продвинутые сценарии:

  • Объединение токенов: Полезно, когда операция должна реагировать на несколько источников отмены (например, пользовательский запрос + общий таймаут приложения).
    
    var userCancellation = new CancellationTokenSource();
    var timeoutCancellation = new CancellationTokenSource(TimeSpan.FromSeconds(30));

using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource( userCancellation.Token, timeoutCancellation.Token );

await ProcessDataAsync(linkedCts.Token);


*   **Регистрация callback при отмене:**
```csharp
cancellationToken.Register(() => 
{
    Console.WriteLine("Токен отменен, выполняется очистка...");
    // Освобождение неуправляемых ресурсов и т.д.
});

Важные принципы:

  • Отмена является кооперативной. Метод должен сам проверять токен и корректно завершаться.
  • После вызова Cancel() соответствующий CancellationToken переходит в состояние "отменен" и больше не может быть использован заново.
  • Многие API .NET (например, Task.Delay, HttpClient.SendAsync, EF Core ToListAsync) имеют перегрузки, принимающие CancellationToken.