CodeGym /Cursos /C# SELF /Cancelamento de operações:

Cancelamento de operações: CancellationToken

C# SELF
Nível 60 , Lição 4
Disponível

1. Introdução ao CancellationToken

Imagine a situação: você inicia um download grande e de repente lembra que seu plano de Internet é limitado e cada gigabyte custa caro. Ou o usuário se confundiu, começou um cálculo e depois resolveu que não precisa mais. Ele quer apertar «Cancelar» e não esperar até o fim. Aplicativos modernos precisam ser responsivos, e para isso devemos conseguir enviar um sinal de parada a qualquer momento e interromper corretamente a operação assíncrona.

Para isso, no .NET existe toda uma infraestrutura de cancelamento — o CancellationToken.

CancellationToken (literalmente "token de cancelamento") é um objeto especial que você passa para dentro da sua operação longa. A qualquer momento você pode sinalizar o cancelamento através do CancellationTokenSource, e a operação deve periodicamente checar o token (por exemplo, IsCancellationRequested) e terminar corretamente, se necessário chamando ThrowIfCancellationRequested().

Como ele é por dentro? (Resumo)

  • Existe o objeto CancellationTokenSource, que "gera" tokens e sabe cancelá-los (método Cancel()).
  • O próprio CancellationToken é uma "bandeira de sinalização" que pode ser distribuída para várias operações (via propriedade Token do source).
  • A operação checa periodicamente o token: se o cancelamento foi requisitado, ela para o trabalho (ou lança OperationCanceledException).

Analogia: você é o chefe (você é o CancellationTokenSource). Você distribui crachás aos seus subordinados (esses são os CancellationToken). Quando você decide que é hora de cancelar tudo, você levanta a bandeira vermelha — e todos que têm crachá recuam imediatamente, deixando o almoço pela metade.

2. Como usar o CancellationToken

Criar uma source do token de cancelamento (CancellationTokenSource)

var cts = new CancellationTokenSource();

Obter o próprio token (CancellationToken)

CancellationToken token = cts.Token;

Passar o token para um método assíncrono

A maioria dos métodos assíncronos padrão do .NET aceita um parâmetro do tipo CancellationToken. Por exemplo, HttpClient.GetAsync, Stream.ReadAsync, Task.Delay etc.

Exemplo — delay com cancelamento:

await Task.Delay(10000, token); // Esperar 10 segundos — mas pode ser cancelado!

Solicitar cancelamento (por exemplo, por botão ou por timer)

cts.Cancel(); // Todas as operações que receberam esse token vão saber do cancelamento 

Checar o token dentro do método

Dentro dos seus métodos (especialmente se o trabalho for longo e cíclico) é necessário verificar regularmente a flag de cancelamento e lançar a exceção OperationCanceledException se o cancelamento foi pedido:

token.ThrowIfCancellationRequested();

Ou simplesmente checar a propriedade:

if (token.IsCancellationRequested)
{
    // Liberamos recursos, saímos do método
}

3. Exemplo: Vamos adicionar cancelamento ao nosso app de estudo

Suponha que temos um app que baixa dados de um site. Vamos adicionar a possibilidade de cancelar o download se o usuário mudar de ideia.

Exemplo básico de download assíncrono

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

class Downloader
{
    public async Task DownloadAsync(string url)
    {
        var client = new HttpClient();
        string content = await client.GetStringAsync(url); // Sem cancelamento
        Console.WriteLine("Download concluído!");
    }
}

Adicionando CancellationToken

public async Task DownloadAsync(string url, CancellationToken token)
{
    var client = new HttpClient();
    string content = await client.GetStringAsync(url, token); // Agora com suporte a cancelamento!
    Console.WriteLine("Download concluído!");
}

Controlando o cancelamento do código chamador

static async Task Main(string[] args)
{
    var downloader = new Downloader();
    var cts = new CancellationTokenSource();

    Console.WriteLine("Digite a URL para download:");
    string url = Console.ReadLine();

    var downloadTask = downloader.DownloadAsync(url, cts.Token);

    Console.WriteLine("Pressione qualquer tecla para cancelar o download...");
    Console.ReadKey();

    cts.Cancel(); // Sinal de cancelamento

    try
    {
        await downloadTask;
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("Download cancelado pelo usuário!");
    }
}

É simples assim! Agora o usuário pode interromper a operação a qualquer momento.

4. Interação entre CancellationTokenSource e métodos

flowchart TD
    A["Código do usuário (Main)"] -- cria --> B["CancellationTokenSource"]
    B -- emite --> C["CancellationToken"]
    C -- é passado para --> D["Operação assíncrona"]
    A -- chama Cancel() --> B
    D -- checa periodicamente --> C
    C -- sinaliza o cancelamento para --> D
    D -- lança Exception ou termina o trabalho --> A

5. Tratamento do cancelamento

Quando a operação recebe um token de cancelamento, existem duas formas de comportamento:

Métodos .NET lançam a exceção por conta própria.
Se você chamar métodos padrão, como Stream.ReadAsync, HttpClient.GetAsync ou Task.Delay, e passar o token, — assim que Cancel() for chamado, esses métodos vão lançar OperationCanceledException por conta própria. Só resta você capturar essa exceção.

Código assíncrono customizado.
Se você implementar uma operação longa por conta própria, por exemplo processamento em loop ou cálculos pesados, é sua responsabilidade checar regularmente token.IsCancellationRequested (ou chamar token.ThrowIfCancellationRequested()) para reagir ao cancelamento corretamente.

Exemplo: operação longa com verificação manual de cancelamento

public async Task CalculatePrimesAsync(int max, CancellationToken token)
{
    for (int i = 2; i < max; i++)
    {
        token.ThrowIfCancellationRequested(); // Checa o cancelamento

        if (IsPrime(i))
        {
            Console.WriteLine($"Número primo: {i}");
            await Task.Delay(100, token); // Dá um "descanso" (pode ser cancelado)
        }
    }
    Console.WriteLine("Cálculo concluído!");
}

private bool IsPrime(int n)
{
    for (int i = 2; i <= Math.Sqrt(n); i++)
        if (n % i == 0) return false;
    return true;
}

6. Dicas úteis

Métodos e classes que suportam CancellationToken

Classe/método Suporta CancellationToken? Exemplo de uso
Task.Delay
await Task.Delay(1000, token);
HttpClient.GetAsync
await client.GetAsync(url, token);
Stream.ReadAsync
await stream.ReadAsync(buffer, 0, len, token);
Task.Run
Task.Run(() => ..., token);
File.ReadAllTextAsync
await File.ReadAllTextAsync(path, token);
Thread.Sleep
Não suporta; é melhor usar Task.Delay
Seus métodos ✔ (se você adicionar suporte!)
MyLongMethod(token)

Lifecycle de uma operação cancelável

sequenceDiagram
    participant Usuário
    participant Main
    participant CancellationTokenSource
    participant OperaçãoAssíncrona

    Usuário->>Main: Inicia operação
    Main->>CancellationTokenSource: Cria CTS
    Main->>OperaçãoAssíncrona: Inicia e passa o CancellationToken
    Usuário->>Main: Aperta "Cancelar"
    Main->>CancellationTokenSource: Chama Cancel()
    OperaçãoAssíncrona->>OperaçãoAssíncrona: Nota o cancelamento (\nIsCancellationRequested)
    OperaçãoAssíncrona-->>Main: Lança OperationCanceledException
    Main->>Usuário: Mostra mensagem "Operação cancelada"

Onde isso é usado na prática

  • Aplicações UI: Interromper downloads longos, cálculos, trabalho com arquivos se o usuário quiser fechar a janela ou cancelar a ação.
  • Aplicações servidoras: Se o cliente cair a conexão — é melhor cancelar o processamento do request para não desperdiçar recursos.
  • Processamento de grandes volumes: Tarefas podem levar muito tempo — sempre dê a opção de parar cálculos ou migrações.
  • Integração com hardware: Scans, impressão e outras operações às vezes precisam ser abortadas urgentemente — suporte a cancelamento é obrigatório.

7. Erros típicos ao trabalhar com CancellationToken

Erro №1: Ignorar a checagem do token.
Se a operação não checa token.IsCancellationRequested ou não chama ThrowIfCancellationRequested(), ela não vai parar quando cancelarem, consumindo recursos desnecessários.

Erro №2: Tratamento incorreto de OperationCanceledException.
Se você não capturar OperationCanceledException, o app pode terminar de forma inesperada. Sempre use try-catch para lidar com cancelamento.

Erro №3: Gerenciamento errado de recursos ao cancelar.
O cancelamento não desfaz mudanças automaticamente (por exemplo, em arquivos ou banco de dados). É preciso limpar recursos manualmente no bloco catch.

Erro №4: Passar um token já cancelado.
Se o token já estiver cancelado, o método vai lançar a exceção imediatamente, o que pode quebrar a lógica se isso não for previsto.

2
Tarefa
C# SELF, nível 60, lição 4
Bloqueado
Busca de números primos com suporte a cancelamento
Busca de números primos com suporte a cancelamento
1
Pesquisa/teste
Comparando Task e Thread, nível 60, lição 4
Indisponível
Comparando Task e Thread
Processamento paralelo de dados
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION