1. Introduction
Quand vous lancez une opération asynchrone (ou longue), l'utilisateur (ou un autre morceau de code) peut soudain décider : "Stop ! Plus besoin ! Arrête-toi !". Par exemple, l'utilisateur peut vouloir interrompre le téléchargement d'un fichier énorme, fermer la fenêtre du programme ou changer d'avis pour une recherche dans une grosse base de données. Sans prise en charge de l'annulation, votre programme peut continuer à tourner et consommer des ressources inutilement — pas très sympa pour l'utilisateur ni pour l'ordinateur.
Scénarios typiques d'annulation :
- Annulation du téléchargement d'un fichier ou annulation de l'envoi de données.
- Sortie rapide d'un traitement de données complexe à la demande de l'utilisateur.
- Interruption/arrêt brutal d'une tâche longue qui n'est plus pertinente.
L'annulation — c'est votre ingrédient secret pour des applications réactives, conviviales et économes.
Comment annuler des opérations asynchrones en .NET ?
En .NET, on utilise la notion de "token d'annulation" (CancellationToken) pour arrêter des tâches longues. C'est un objet spécial qu'on passe à tous les composants d'une tâche. Si quelqu'un demande l'annulation — le token signale immédiatement à toutes les parties intéressées. En pratique, c'est comme un drapeau rouge : qui le voit en premier s'arrête.
En .NET, ce mécanisme est implémenté par deux classes clés :
- CancellationTokenSource — crée et gère le token d'annulation.
- CancellationToken — est passé aux méthodes asynchrones pour pouvoir les annuler.
Important : le token d'annulation lui-même n'interrompt pas l'exécution du code, il se contente de "signaler" — c'est votre application qui décide quand et comment réagir à ce signal.
2. Créons un token d'annulation et annulons une tâche
Voyons comment cela fonctionne avec un exemple simple (on va développer notre application console pédagogique).
Exemple : opération asynchrone simple avec annulation
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("Appuyez sur une touche pour annuler l'opération...");
Console.ReadKey();
// Request cancellation
cts.Cancel();
try
{
await longRunningTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("L'opération a été annulée !");
}
}
// Asynchronous method, that supports cancellation
static async Task DoWorkAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
// Check for a cancellation signal*
cancellationToken.ThrowIfCancellationRequested();
Console.WriteLine($"Exécution de l'étape {i + 1}/10...");
await Task.Delay(1000); // Delay 1 second
}
Console.WriteLine("L'opération s'est terminée avec succès !");
}
}
}
Comment ça marche ?
- Nous créons un CancellationTokenSource (cts), d'où on obtient le token (cts.Token).
- Nous passons ce token à notre opération asynchrone.
- À l'intérieur de DoWorkAsync(), on vérifie régulièrement le token avec la méthode ThrowIfCancellationRequested(). Si l'utilisateur demande l'annulation — la méthode lance une OperationCanceledException, et la tâche s'arrête.
- Dans Main(), on attend l'appui sur une touche et on appelle cts.Cancel() pour "signaler" l'arrêt de l'opération.
Si vous ne vérifiez pas cancellationToken.IsCancellationRequested ou n'appelez pas ThrowIfCancellationRequested(), votre tâche continuera à fonctionner comme si de rien n'était — le token n'est qu'un drapeau informatif.
3. CancellationToken : comment c'est fait ? Et un peu de magie
Le token d'annulation est un objet qu'on peut facilement passer entre méthodes et tâches. Ça donne beaucoup de flexibilité :
- Le même token peut être utilisé dans plusieurs opérations asynchrones et synchrones.
- On peut organiser une annulation "de groupe" pour toutes les tâches si plusieurs opérations utilisent le token issu d'un même CancellationTokenSource.
- Le token est discret : même si vous l'ignorez, le code continuera de fonctionner comme avant.
Gérer l'annulation : où et comment vérifier le token ?
Vous devez vérifier si le "drapeau d'annulation" a été levé aux endroits logiques : dans les boucles, à chaque étape d'un long traitement, lors des transitions entre étapes, etc.
// At any point during the checks
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("Opération annulée ! On sort...");
return;
}
// Or like this (shorter, with an exception being thrown)
cancellationToken.ThrowIfCancellationRequested();
On utilise généralement ThrowIfCancellationRequested() — il lance une exception spéciale qui peut être interceptée dans le code appelant.
4. Méthodes asynchrones de la bibliothèque standard
Beaucoup de classes et méthodes .NET (surtout asynchrones) supportent CancellationToken directement "out of the box". Il faut les utiliser pour arrêter correctement les opérations.
Voici un exemple de lecture de fichier asynchrone :
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)
{
// Process the data...
// If a cancellation token was requested, ReadAsync itself will throw an OperationCanceledException
}
}
}
Plus d'infos sur FileStream.ReadAsync et CancellationToken dans la documentation officielle.
5. Astuces utiles
Timeout — c'est aussi une annulation !
Vous pouvez annuler automatiquement des opérations après un délai donné. Pour ça, on peut "programmer" le CancellationToken :
// Create CancellationTokenSource with timeout (for example, 5 seconds)
CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
Au bout de 5 secondes, le token sera automatiquement "levé" et toutes les opérations l'utilisant s'arrêteront à la prochaine vérification. Très pratique si vous ne voulez pas attendre indéfiniment.
Que se passe-t-il lors d'une annulation ?
Quand vous appelez Cancel() sur le source du token, toutes les méthodes qui utilisent ce token et vérifient son état apprennent la nouvelle. Mais si votre code ne vérifie pas le token — l'annulation ne se produira pas.
Erreur typique : oublier de passer le token à toutes les opérations asynchrones et longues. Alors une partie sera annulée et une autre continuera comme si de rien n'était.
Visualisation : comment l'annulation se déroule
sequenceDiagram
participant Main as Thread principal
participant CTS as CancellationTokenSource
participant Task as Tâche asynchrone
Main->>CTS: crée CTS, obtient le token
Main->>Task: passe le token à la tâche asynchrone
Note over Task: La tâche vérifie périodiquement le token
Main->>CTS: appelle Cancel()
CTS-->>Task: le token reçoit le statut "annulé"
Task-->>Main: lance OperationCanceledException
Main->>Main: attrape l'exception et termine le travail
Où l'annulation est-elle utilisée ?
- Téléchargements asynchrones et requêtes vers des serveurs : on peut annuler en cas de connexion lente ou si l'utilisateur abandonne.
- Calculs lourds : on peut arrêter via timeout ou à la demande de l'utilisateur.
- Opérations réseau, gestion de fichiers, traitement de grosses collections en background.
Voilà pour la présentation de l'annulation des opérations asynchrones — votre application sera désormais non seulement rapide, mais aussi attentionnée !
6. Conseils et erreurs typiques
N'oubliez pas de passer le token d'annulation à toutes les méthodes et appels qui supportent la cancellation. Si vous l'omettez quelque part — l'opération peut "se coincer" et ne pas s'arrêter.
Vérifiez régulièrement le token — surtout dans les longues boucles, traitements de fichiers ou lors de téléchargements de gros volumes. Utilisez IsCancellationRequested ou ThrowIfCancellationRequested().
Ne tentez pas de "forcer" l'arrêt d'un thread ou d'une tâche depuis l'extérieur : le token d'annulation est une demande polie d'arrêt, pas un marteau pour frapper le thread.
Les fonctions de la bibliothèque standard, comme ReadAsync, Delay, HttpClient.SendAsync et beaucoup d'autres, supportent déjà l'annulation via token. Utilisez-les !
Lors de la gestion de l'annulation, capturez spécifiquement OperationCanceledException — c'est l'exception qui indique une annulation correcte suite à une requête.
GO TO FULL VERSION