1. Getting to know CancellationToken
Imagine this: you start a long file download and then suddenly remember your Internet plan is metered and gigabytes are precious. Or a user got confused, started a computation, then decided they don't need it. They want to hit "Cancel" and not wait. Modern apps need to be responsive, so we must be able to send a stop signal at any time and correctly interrupt an async operation.
That's exactly what the cancellation infrastructure in .NET is for — the CancellationToken.
CancellationToken (literally "cancellation token") is a special object you pass into your long-running operation. At any point you can signal cancellation via a CancellationTokenSource, and the operation should periodically check the token (for example, IsCancellationRequested) and terminate cleanly, calling ThrowIfCancellationRequested() when needed.
How is it organized? (Short)
- There is a CancellationTokenSource object that "creates" tokens and can cancel them (method Cancel()).
- The CancellationToken itself is a "signal flag" that you can hand out to many operations (via the Token property on the source).
- An operation periodically checks the token: if cancellation is requested, it stops working (or throws OperationCanceledException).
Analogy: you're the boss (you are the CancellationTokenSource). You give your team "badges" (those are CancellationTokens). When you decide it's time to cancel everything, you raise the red flag — and everyone with a badge retreats immediately, leaving their half-eaten lunch.
2. How to use CancellationToken
Create a cancellation token source (CancellationTokenSource)
var cts = new CancellationTokenSource();
Get the token itself (CancellationToken)
CancellationToken token = cts.Token;
Pass the token into an async method
Most built-in .NET async methods accept a parameter of type CancellationToken. For example, HttpClient.GetAsync, Stream.ReadAsync, Task.Delay, and others.
Example — a cancellable delay:
await Task.Delay(10000, token); // Wait 10 seconds — but it can be canceled!
Request cancellation (for example, by a button or a timer)
cts.Cancel(); // All operations that received this token will learn about the cancellation
Check the token inside a method
Inside your methods (especially if the work is long and iterative) you need to regularly check the cancellation flag and throw OperationCanceledException if cancellation is requested:
token.ThrowIfCancellationRequested();
Or just check the property:
if (token.IsCancellationRequested)
{
// Release resources, exit the method
}
3. Example: Let's add cancellation to our tutorial app
Suppose we have an app that downloads data from a website. Let's add the ability to cancel the download if the user changes their mind.
Basic async download example
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); // Without cancellation
Console.WriteLine("Download completed!");
}
}
Adding CancellationToken
public async Task DownloadAsync(string url, CancellationToken token)
{
var client = new HttpClient();
string content = await client.GetStringAsync(url, token); // Now with cancellation support!
Console.WriteLine("Download completed!");
}
Controlling cancellation from the caller
static async Task Main(string[] args)
{
var downloader = new Downloader();
var cts = new CancellationTokenSource();
Console.WriteLine("Enter URL to download:");
string url = Console.ReadLine();
var downloadTask = downloader.DownloadAsync(url, cts.Token);
Console.WriteLine("Press any key to cancel the download...");
Console.ReadKey();
cts.Cancel(); // Cancellation signal
try
{
await downloadTask;
}
catch (OperationCanceledException)
{
Console.WriteLine("Download canceled by the user!");
}
}
That’s it! Now the user can interrupt the operation at any time.
4. Interaction between CancellationTokenSource and methods
flowchart TD
A["User code (Main)"] -- creates --> B["CancellationTokenSource"]
B -- issues --> C["CancellationToken"]
C -- passed to --> D["Async operation"]
A -- calls Cancel() --> B
D -- periodically checks --> C
C -- signals cancellation to --> D
D -- throws Exception or finishes work --> A
5. Handling cancellation
When an operation receives a cancellation token, there are two behavior options:
.NET methods will throw the exception themselves.
If you call standard methods like Stream.ReadAsync, HttpClient.GetAsync, or Task.Delay, and pass them the token — as soon as Cancel() is called, those methods will throw OperationCanceledException on their own. You just need to catch that exception.
Your custom async code.
If you implement a long-running operation yourself, e.g., iterative processing or heavy calculations, it's your responsibility to regularly check token.IsCancellationRequested (or call token.ThrowIfCancellationRequested()) to react to cancellation properly.
Example: a "long" operation with manual cancellation checks
public async Task CalculatePrimesAsync(int max, CancellationToken token)
{
for (int i = 2; i < max; i++)
{
token.ThrowIfCancellationRequested(); // Check for cancellation
if (IsPrime(i))
{
Console.WriteLine($"Prime number: {i}");
await Task.Delay(100, token); // Let it "rest" (can be canceled)
}
}
Console.WriteLine("Calculation finished!");
}
private bool IsPrime(int n)
{
for (int i = 2; i <= Math.Sqrt(n); i++)
if (n % i == 0) return false;
return true;
}
6. Useful nuances
Methods and classes that support CancellationToken
| Class/method | Supports CancellationToken? | Usage example |
|---|---|---|
|
✔ | |
|
✔ | |
|
✔ | |
|
✔ | |
|
✔ | |
|
✖ | Does not support it; better to use Task.Delay |
| Your methods | ✔ (if you add support!) | |
Lifecycle of a cancellable operation
sequenceDiagram
participant User
participant Main
participant CancellationTokenSource
participant AsyncOperation
User->>Main: Start operation
Main->>CancellationTokenSource: Create CTS
Main->>AsyncOperation: Start and pass CancellationToken
User->>Main: Presses "Cancel"
Main->>CancellationTokenSource: Calls Cancel()
AsyncOperation->>AsyncOperation: Notices cancellation (\nIsCancellationRequested)
AsyncOperation-->>Main: Throws OperationCanceledException
Main->>User: Shows message "Operation canceled"
Where it's used in real life
- UI apps: Abort long downloads, computations, file work if the user wants to close the window or cancel an action.
- Server apps: If a client drops the connection — it's better to cancel request processing right away to avoid wasting resources.
- Big data processing: Tasks can be very long — always provide a way to stop calculations or migrations.
- Hardware integration: Scanning, printing and other operations sometimes need an urgent stop — cancellation support is mandatory there.
7. Common mistakes when working with CancellationToken
Mistake #1: Ignoring token checks.
If an operation doesn't check token.IsCancellationRequested or call ThrowIfCancellationRequested(), it won't stop on cancellation and will waste resources.
Mistake #2: Mishandling OperationCanceledException.
If you don't catch OperationCanceledException, the app may terminate unexpectedly. Always use try-catch to handle cancellation.
Mistake #3: Improper resource management on cancellation.
Cancellation does not automatically roll back changes (e.g., files or DB). You need to manually clean up resources in a catch block.
Mistake #4: Passing an already canceled token.
If the token is already canceled, the method will throw immediately, which can break logic if not accounted for.
GO TO FULL VERSION