← Назад к вопросам

Какие знаешь способы отмены задачи?

2.0 Middle🔥 231 комментариев
#Асинхронность и многопоточность

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы отмены задач в C#

В C# существует несколько механизмов отмены асинхронных и длительных операций, которые позволяют корректно прерывать выполнение задач. Основные подходы основаны на использовании CancellationToken и связанных с ним конструкциях.

1. Использование CancellationTokenSource и CancellationToken

Это основной и наиболее рекомендуемый способ. CancellationTokenSource генерирует токены отмены, которые передаются в методы, поддерживающие отмену.

using System;
using System.Threading;
using System.Threading.Tasks;

public class CancellationExample
{
    public static async Task ProcessWithCancellationAsync()
    {
        // Создаем источник токена отмены
        var cts = new CancellationTokenSource();
        
        // Запускаем фоновую задачу для отмены через 3 секунды
        _ = Task.Delay(3000).ContinueWith(_ => 
        {
            cts.Cancel();
            Console.WriteLine("Cancellation requested");
        });
        
        try
        {
            await LongRunningOperationAsync(cts.Token);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation was cancelled");
        }
    }
    
    private static async Task LongRunningOperationAsync(CancellationToken token)
    {
        for (int i = 0; i < 100; i++)
        {
            // Проверяем, не запрошена ли отмена
            token.ThrowIfCancellationRequested();
            
            await Task.Delay(100, token);
            Console.WriteLine($"Processing item {i}");
        }
    }
}

2. Связывание нескольких источников отмены

Можно объединять несколько токенов отмены, используя CancellationTokenSource.CreateLinkedTokenSource().

public static async Task LinkedCancellationAsync(
    CancellationToken externalToken1, 
    CancellationToken externalToken2)
{
    using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
        externalToken1, externalToken2);
    
    try
    {
        await ProcessDataAsync(linkedCts.Token);
    }
    catch (OperationCanceledException ex)
    {
        // Определяем, какой токен вызвал отмену
        if (externalToken1.IsCancellationRequested)
            Console.WriteLine("Cancelled by first token");
        else if (externalToken2.IsCancellationRequested)
            Console.WriteLine("Cancelled by second token");
    }
}

3. Отмена с таймаутом

CancellationTokenSource поддерживает автоматическую отмену через заданное время.

public static async Task ProcessWithTimeoutAsync()
{
    // Токен автоматически отменится через 5 секунд
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
    
    try
    {
        await DownloadLargeFileAsync(cts.Token);
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Download cancelled due to timeout");
    }
}

4. Отмена через регистрацию колбэков

Можно зарегистрировать callback-функции, которые выполнятся при отмене операции.

public static Task ProcessWithCallbacksAsync(CancellationToken token)
{
    var tcs = new TaskCompletionSource<bool>();
    
    // Регистрируем колбэк для очистки ресурсов
    token.Register(() =>
    {
        CleanupResources();
        tcs.TrySetCanceled(token);
    });
    
    return ProcessOperationAsync(token, tcs);
}

5. Ручная проверка CancellationToken в циклах

Для операций без встроенной поддержки async/await.

public static void ProcessSynchronously(CancellationToken token)
{
    for (int i = 0; i < 1000000; i++)
    {
        if (token.IsCancellationRequested)
        {
            // Выполняем корректное завершение
            Cleanup();
            return; // или throw new OperationCanceledException();
        }
        
        // Выполняем работу
        ProcessItem(i);
    }
}

6. Использование метода CancelAfter()

Динамическая установка таймаута для уже созданного CancellationTokenSource.

public static async Task DynamicCancellationAsync()
{
    var cts = new CancellationTokenSource();
    
    // Устанавливаем таймаут после начала операции
    cts.CancelAfter(TimeSpan.FromSeconds(10));
    
    await ExecuteWithDynamicTimeoutAsync(cts.Token);
}

7. Отмена через CancellationToken в методах Task

Многие методы Task имеют перегрузки с поддержкой CancellationToken.

public static async Task TaskWithCancellationAsync()
{
    var cts = new CancellationTokenSource();
    
    // Task.Delay с поддержкой отмены
    try
    {
        await Task.Delay(TimeSpan.FromSeconds(10), cts.Token);
    }
    catch (TaskCanceledException)
    {
        Console.WriteLine("Task.Delay was cancelled");
    }
    
    // Task.Run с поддержкой отмены
    var task = Task.Run(() => 
    {
        // Работа, которую можно отменить
    }, cts.Token);
}

8. Создание собственных отменяемых операций

При разработке библиотек следует правильно реализовывать поддержку отмены.

public class CustomCancellableOperation
{
    public async Task<int> ComputeAsync(
        CancellationToken cancellationToken = default)
    {
        await Task.Yield();
        
        // Регулярно проверяем токен
        for (int i = 0; i < 100; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            
            // Передаем токен в глубинные вызовы
            await StepAsync(i, cancellationToken);
        }
        
        return 42;
    }
}

Рекомендации по использованию

  1. Всегда передавайте CancellationToken в асинхронные методы, если они поддерживают длительные операции
  2. Используйте ThrowIfCancellationRequested() для быстрой проверки отмены
  3. Корректно обрабатывайте OperationCanceledException на верхних уровнях
  4. Освобождайте ресурсы в блоках finally или через using
  5. Избегайте использования Thread.Abort() - это устаревший и опасный подход
  6. Предоставляйте параметр по умолчанию в публичных API: CancellationToken cancellationToken = default

Правильная обработка отмены задач критически важна для создания отзывчивых и надежных приложений, особенно в серверных сценариях, где необходимо управлять временем выполнения операций и ресурсами.