1. Introducción
Cuando arrancas una operación asíncrona (o larga), el usuario (u otro código) puede de repente decidir: "¡Alto! Ya no hace falta. ¡Para!". Por ejemplo, el usuario decidió interrumpir la descarga de un archivo enorme, cerró la ventana del programa o cambió de idea sobre buscar información en una base de datos grande. Sin soporte de cancelación tu programa puede seguir funcionando y consumir recursos inútilmente — no es la mejor muestra de respeto hacia el usuario y el equipo.
Escenarios típicos de cancelación:
- Cancelar la descarga de un archivo o cancelar el envío de datos.
- Salir rápidamente de un proceitselfiento complejo por petición del usuario.
- Al pausar/bruscamente detener una tarea larga que ya no es relevante.
La cancelación es tu ingrediente secreto para aplicaciones amables, responsivas y eficientes.
¿Cómo cancelar operaciones asíncronas en .NET?
En .NET para cancelar tareas largas se usa el concepto de "token de cancelación" (CancellationToken). Es un objeto especial que se pasa a los componentes de la tarea. Si alguien pide cancelar la operación — el token informa al instante a todas las partes interesadas. En la práctica es como una bandera roja: quien la vea primero, se detiene.
En .NET este mecanismo está implementado con dos clases clave:
- CancellationTokenSource — crea y administra el token de cancelación.
- CancellationToken — se pasa a métodos asíncronos para que puedan ser cancelados.
Importante: el token por sí solo no interrumpe la ejecución del código; solo "señaliza" — y tu aplicación decide cuándo y cómo reaccionar a esa señal.
2. Creamos un token de cancelación y cancelamos una tarea
Veamos cómo funciona con un ejemplo sencillo (vamos a ampliar nuestra aplicación de consola educativa).
Ejemplo: operación asíncrona simple con cancelación
using System;
using System.Threading;
using System.Threading.Tasks;
namespace DemoApp
{
class Program
{
static async Task Main()
{
// Create a cancellation token source
CancellationTokenSource cts = new CancellationTokenSource();
// Start an asynchronous task
Task longRunningTask = DoWorkAsync(cts.Token);
Console.WriteLine("Press any key to cancel the operation...");
Console.ReadKey();
// Request cancellation otmenu
cts.Cancel();
try
{
await longRunningTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation was cancelled byla otmenena!");
}
}
// An asynchronous method that supports cancellation
static async Task DoWorkAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
// Check cancellation signal
cancellationToken.ThrowIfCancellationRequested();
Console.WriteLine($"Step execution {i + 1}/10...");
await Task.Delay(1000); // Delay 1 second
}
Console.WriteLine("Operation was cancelled zavershena uspeshno!");
}
}
}
¿Cómo funciona esto?
- Creamos CancellationTokenSource (cts), desde donde obtenemos el token (cts.Token).
- Paitselfos ese token a nuestra operación asíncrona.
- Dentro de DoWorkAsync() comprobamos regularmente el token con ThrowIfCancellationRequested(). Si el usuario pide cancelar la tarea — ese método lanzará la excepción OperationCanceledException, y la tarea se interrumpirá.
- En Main() esperamos a que se pulse cualquier tecla y llamamos a cts.Cancel() para "señalar" la necesidad de detener la operación.
Si no compruebas cancellationToken.IsCancellationRequested o no llamas a ThrowIfCancellationRequested(), tu tarea seguirá funcionando como si nada — el token es solo una bandera informativa.
3. CancellationToken: ¿cómo está hecho? Y un poco de magia
El token de cancelación es un objeto que se puede pasar fácilmente entre métodos y tareas. Esto da mucha flexibilidad:
- El mismo token se puede usar en varias operaciones asíncronas y sincrónicas.
- Puedes organizar una "cancelación grupal" para todas las tareas si varias operaciones usan el token proveniente de un mismo CancellationTokenSource.
- El token es no intrusivo: incluso si lo ignoras, el código seguirá funcionando igual.
Gestión de la cancelación: ¿dónde y cómo comprobar el token?
Comprobar si la "bandera de cancelación" está levantada se debe hacer donde tenga sentido lógico: en un bucle, en cada paso de un proceitselfiento largo, al pasar de una etapa a otra, etc.
// At any point during the check
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Operation was cancelled otmenena! Vykhodim...");
return;
}
// Or like this (shortly throwing exception)
cancellationToken.ThrowIfCancellationRequested();
Normalmente se usa ThrowIfCancellationRequested() — lanza una excepción especial que se puede atrapar en el código que llamó.
4. Métodos asíncronos de la biblioteca estándar
Muchísimas clases y métodos de .NET (especialmente los asíncronos) soportan CancellationToken "de serie". Debes usarlos para detener operaciones de forma "correcta".
Aquí un ejemplo de lectura asíncrona de archivo:
using System.IO;
using System.Threading;
using System.Threading.Tasks;
class FileDemo
{
public static async Task ReadFileWithCancelAsync(string filePath, CancellationToken cancellationToken)
{
using FileStream stream = File.OpenRead(filePath);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
{
// Processing data...
// If cancellation was requested, ReadAsync itself throw OperationCanceledException
}
}
}
Lee más sobre FileStream.ReadAsync y CancellationToken en la documentación oficial.
5. Matices útiles
Timeout — ¡también es cancelación!
Puedes cancelar operaciones automáticamente tras un tiempo definido. Para eso puedes "programar" el token de cancelación:
// Create CancellationTokenSource with timeout (for example, 5 seconds)
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
Tras 5 segundos el token se marcará automáticamente y todas las operaciones con él se detendrán en la siguiente comprobación. Es muy útil si no quieres esperar eternamente.
¿Qué pasa al cancelar?
Cuando llamas Cancel() en el source del token, todos los métodos que usan ese token y comprueban su estado se enteran del cambio. Pero si tu código no comprueba el token — la cancelación no ocurrirá.
Un error típico: olvidar pasar el token a todas las operaciones asíncronas y largas. Entonces una parte se cancelará y otra seguirá como si nada.
Visualización: cómo transcurre la cancelación
sequenceDiagram
participant Main as Hilo principal
participant CTS as CancellationTokenSource
participant Task as Tarea asíncrona
Main->>CTS: crea CTS, obtiene token
Main->>Task: pasa token a la tarea asíncrona
Note over Task: La tarea comprueba el token periódicamente
Main->>CTS: llama a Cancel()
CTS-->>Task: el token recibe el estado "cancelado"
Task-->>Main: lanza OperationCanceledException
Main->>Main: atrapa la excepción y termina el trabajo
¿Dónde se aplica la cancelación de operaciones asíncronas?
- Descargas asíncronas y peticiones al servidor: se pueden cancelar con mala conexión o si el usuario se cansa.
- Cálculos pesados: se pueden detener por timeout o por petición del usuario.
- Operaciones de red, trabajo con archivos, proceitselfiento de grandes colecciones en background.
Con esto termina la introducción a la cancelación de operaciones asíncronas — ahora tu aplicación será no solo rápida, sino también considerada.
6. Consejos y errores típicos
No olvides pasar el token de cancelación a todos los métodos y llamadas que soporten cancellation. Si lo olvidas en algún sitio — la operación puede "quedarse colgada" y no detenerse.
Comprueba el token regularmente — especialmente en bucles largos, procesando archivos o descargando grandes volúmenes. Usa IsCancellationRequested o ThrowIfCancellationRequested().
No intentes "forzar" la terminación de un hilo o tarea desde fuera: el token de cancelación es una petición para que se detengan, no un garrote para pegar al hilo.
Las funciones de la biblioteca estándar como ReadAsync, Delay, HttpClient.SendAsync y muchas otras ya soportan cancelación vía token. ¡Úsalas!
Al manejar la cancelación captura específicamente OperationCanceledException — es la excepción que indica una cancelación correcta por petición.
GO TO FULL VERSION