1. Rencontre avec CancellationToken
Imaginez la situation : vous lancez un téléchargement long, puis vous vous rappelez que votre connexion est limitée et que les gigaoctets coûtent cher. Ou l'utilisateur s'est trompé, a lancé un calcul, puis a décidé que ce n'était pas nécessaire. Il veut appuyer sur « Annuler » et ne pas attendre la fin. Les applications modernes doivent être réactives, et pour ça on doit pouvoir envoyer un signal d'arrêt à tout moment et interrompre proprement une opération asynchrone.
C'est précisément pour ça qu'il existe toute une infrastructure d'annulation en .NET — le CancellationToken.
CancellationToken (littéralement « token d'annulation ») est un objet spécial que vous passez à l'intérieur de votre opération longue. À tout moment vous pouvez signaler l'annulation via un CancellationTokenSource, et l'opération doit vérifier périodiquement le token (par exemple IsCancellationRequested) et se terminer proprement, si nécessaire en appelant ThrowIfCancellationRequested().
Comment c'est construit ? (Bref)
- Il y a un objet CancellationTokenSource qui « génère » des tokens et sait les annuler (méthode Cancel()).
- Le CancellationToken lui-même est un « drapeau de signalisation » qu'on peut distribuer à plusieurs opérations (via la propriété Token sur la source).
- L'opération vérifie périodiquement le token : si l'annulation est demandée, elle sort du travail (ou lance une OperationCanceledException).
Analogie : vous êtes le patron (vous êtes le CancellationTokenSource). Vous distribuez des « badges » à vos employés (ce sont les CancellationToken). Quand vous décidez qu'il est temps d'annuler tout, vous levez le drapeau rouge — et tous ceux qui ont un badge s'en vont aussitôt, laissant leur déjeuner à moitié mangé.
2. Comment utiliser CancellationToken
Créer une source de token d'annulation (CancellationTokenSource)
var cts = new CancellationTokenSource();
Obtenir le token lui‑même (CancellationToken)
CancellationToken token = cts.Token;
Passer le token à une méthode asynchrone
La plupart des méthodes asynchrones standard de .NET acceptent un paramètre de type CancellationToken. Par exemple, HttpClient.GetAsync, Stream.ReadAsync, Task.Delay, etc.
Exemple — délai avec annulation :
await Task.Delay(10000, token); // Attendre 10 secondes — mais on peut annuler !
Demander l'annulation (par exemple via un bouton ou un timer)
cts.Cancel(); // Toutes les opérations qui ont reçu ce token apprendront l'annulation
Vérifier le token à l'intérieur de la méthode
À l'intérieur de vos méthodes (surtout si le travail est long et itératif) il faut vérifier régulièrement le drapeau d'annulation et lancer l'exception OperationCanceledException si l'annulation est demandée :
token.ThrowIfCancellationRequested();
Ou simplement vérifier la propriété :
if (token.IsCancellationRequested)
{
// Libérer les ressources, sortir de la méthode
}
3. Exemple : Ajoutons l'annulation à notre application pédagogique
Supposons qu'on a une application qui télécharge des données depuis un site. Ajoutons la possibilité d'annuler le téléchargement si l'utilisateur change d'avis.
Exemple basique de téléchargement asynchrone
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); // Sans annulation
Console.WriteLine("Téléchargement terminé !");
}
}
On ajoute le CancellationToken
public async Task DownloadAsync(string url, CancellationToken token)
{
var client = new HttpClient();
string content = await client.GetStringAsync(url, token); // Maintenant avec support d'annulation !
Console.WriteLine("Téléchargement terminé !");
}
Gérer l'annulation depuis le code appelant
static async Task Main(string[] args)
{
var downloader = new Downloader();
var cts = new CancellationTokenSource();
Console.WriteLine("Entrez l'URL à télécharger :");
string url = Console.ReadLine();
var downloadTask = downloader.DownloadAsync(url, cts.Token);
Console.WriteLine("Appuyez sur une touche pour annuler le téléchargement...");
Console.ReadKey();
cts.Cancel(); // Signal d'annulation
try
{
await downloadTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("Téléchargement annulé par l'utilisateur !");
}
}
C'est aussi simple ! Maintenant l'utilisateur peut interrompre l'opération à tout moment.
4. Interaction entre CancellationTokenSource et les méthodes
flowchart TD
A["Code utilisateur (Main)"] -- crée --> B["CancellationTokenSource"]
B -- délivre --> C["CancellationToken"]
C -- est passé --> D["Opération asynchrone"]
A -- appelle Cancel() --> B
D -- vérifie périodiquement --> C
C -- informe de l'annulation --> D
D -- lance Exception ou termine le travail --> A
5. Gestion de l'annulation
Quand une opération reçoit un token d'annulation, il y a deux comportements possibles :
Les méthodes .NET vont lancer l'exception elles‑mêmes.
Si vous appelez des méthodes standard comme Stream.ReadAsync, HttpClient.GetAsync ou Task.Delay en leur passant le token, dès qu'on appellera Cancel() ces méthodes lanceront une OperationCanceledException. Il ne vous reste qu'à intercepter cette exception.
Code asynchrone personnalisé.
Si vous implémentez vous‑même une opération longue, par exemple un traitement en boucle ou des calculs lourds, c'est à vous de vérifier régulièrement token.IsCancellationRequested (ou d'appeler token.ThrowIfCancellationRequested()) pour réagir correctement à l'annulation.
Exemple : opération « longue » avec vérification manuelle de l'annulation
public async Task CalculatePrimesAsync(int max, CancellationToken token)
{
for (int i = 2; i < max; i++)
{
token.ThrowIfCancellationRequested(); // On vérifie l'annulation
if (IsPrime(i))
{
Console.WriteLine($"Nombre premier : {i}");
await Task.Delay(100, token); // On laisse "respirer" (peut être annulé)
}
}
Console.WriteLine("Calcul terminé !");
}
private bool IsPrime(int n)
{
for (int i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
6. Nuances utiles
Méthodes et classes qui supportent CancellationToken
| Classe/méthode | Supporte CancellationToken ? | Exemple d'utilisation |
|---|---|---|
|
✔ | |
|
✔ | |
|
✔ | |
|
✔ | |
|
✔ | |
|
✖ | Ne supporte pas ; mieux vaut utiliser Task.Delay |
| Vos méthodes | ✔ (si vous ajoutez le support !) | |
Cycle de vie d'une opération annulable
sequenceDiagram
participant Utilisateur
participant Main
participant CancellationTokenSource
participant OpérationAsynchrone
Utilisateur->>Main: Démarre l'opération
Main->>CancellationTokenSource: Création du CTS
Main->>OpérationAsynchrone: Lancement et passage du CancellationToken
Utilisateur->>Main: Appuie sur "Annuler"
Main->>CancellationTokenSource: Appelle Cancel()
OpérationAsynchrone->>OpérationAsynchrone: Remarque l'annulation (\nIsCancellationRequested)
OpérationAsynchrone-->>Main: Lance OperationCanceledException
Main->>Utilisateur: Affiche le message "Opération annulée"
Où c'est utilisé dans la vraie vie
- Applications UI : Interrompre de longs téléchargements, calculs, opérations sur fichiers si l'utilisateur veut fermer la fenêtre ou annuler l'action.
- Applications serveur : Si le client coupe la connexion — mieux vaut annuler le traitement de la requête pour ne pas gaspiller des ressources.
- Traitement de grandes données : Les tâches peuvent durer très longtemps — il faut toujours offrir la possibilité d'arrêter les calculs ou les migrations.
- Intégration avec du hardware : Scan, impression et autres opérations peuvent nécessiter un arrêt urgent — le support de l'annulation est indispensable.
7. Erreurs typiques quand on travaille avec CancellationToken
Erreur n°1 : Ignorer la vérification du token.
Si l'opération ne vérifie pas token.IsCancellationRequested ou n'appelle pas ThrowIfCancellationRequested(), elle ne s'arrêtera pas à l'annulation et continuera à consommer des ressources.
Erreur n°2 : Mauvaise gestion de OperationCanceledException.
Si vous n'interceptez pas OperationCanceledException, l'application peut se terminer de façon inattendue. Utilisez toujours un try-catch pour gérer l'annulation.
Erreur n°3 : Mauvaise gestion des ressources lors de l'annulation.
L'annulation ne revient pas en arrière automatiquement (par ex. sur les fichiers ou les bases de données). Il faut nettoyer manuellement les ressources dans le bloc catch.
Erreur n°4 : Passer un token déjà annulé.
Si le token est déjà annulé, la méthode lancera immédiatement une exception, ce qui peut casser la logique si ce n'est pas prévu.
GO TO FULL VERSION