How to Use CancellationTokenSource in C
Cooperative cancellation is a fundamental pattern in .NET for
stopping long-running or asynchronous operations gracefully. This
article explains how to implement cancellation tokens using
CancellationTokenSource in C#, covering token creation,
propagating tokens to asynchronous methods, checking for cancellation
requests, and handling the resulting exceptions.
Understanding the Components
In .NET, cancellation is cooperative, meaning the executing operation must actively monitor and respect a cancellation request. This mechanism relies on two main types:
CancellationTokenSource(CTS): The object responsible for initiating and signaling a cancellation request via itsCancel()orCancelAfter()methods.CancellationToken(CT): A lightweight struct generated by theCancellationTokenSource(via its.Tokenproperty) that is passed into methods to listen for cancellation signals.
Step-by-Step Implementation
Implementing cancellation requires instantiating the source, passing the token, polling or checking the token inside the work method, and signaling the cancellation.
1. Basic Asynchronous Example
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// 1. Create the CancellationTokenSource
using var cts = new CancellationTokenSource();
// 2. Start a background task, passing the Token
Task task = DoWorkAsync(cts.Token);
// Simulate some work in the caller, then cancel
await Task.Delay(2000);
cts.Cancel();
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("The operation was successfully canceled.");
}
}
static async Task DoWorkAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
// 3. Inspect the token and throw if canceled
cancellationToken.ThrowIfCancellationRequested();
Console.WriteLine($"Working... Step {i + 1}");
// Pass the token to built-in async methods
await Task.Delay(1000, cancellationToken);
}
}
}Key Cancellation Techniques
Throwing vs. Checking Manually
There are two primary ways to respond to a token's state inside a method:
cancellationToken.ThrowIfCancellationRequested(): Throws anOperationCanceledExceptionimmediately if cancellation has been requested. This is the standard approach when an aborted operation should result in an exception.cancellationToken.IsCancellationRequested: A boolean property that allows for custom cleanup before exiting without necessarily throwing an exception:
if (cancellationToken.IsCancellationRequested)
{
// Perform custom cleanup here
return;
}Implementing Timeouts
with CancelAfter
CancellationTokenSource can automatically cancel an
operation after a specific duration, which is useful for setting request
timeouts:
// Cancel automatically after 5 seconds
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
// Alternatively, configure an existing source:
// cts.CancelAfter(TimeSpan.FromSeconds(5));Combining Multiple Tokens
When an operation needs to listen to multiple cancellation
sources—such as an external user request combined with an internal
timeout—use CreateLinkedTokenSource:
using var internalTimeoutCts = new CancellationTokenSource(5000);
using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
externalToken,
internalTimeoutCts.Token
);
// linkedCts triggers if either externalToken or internalTimeoutCts cancels
await PerformOperationAsync(linkedCts.Token);Best Practices
- Always dispose
CancellationTokenSource: Instances ofCancellationTokenSourceoften use internal timers (especially when using timeouts), so wrap them in ausingstatement or dispose of them explicitly. - Propagate tokens down the call stack: Forward the
CancellationTokenparameter to every downstream asynchronous method (e.g.,HttpClient.SendAsync,Task.Delay, or database calls) to ensure that the entire call chain terminates promptly. - Catch
OperationCanceledException: Always anticipate and handleOperationCanceledException(orTaskCanceledException) at the boundary where the operation is triggered.