Codectionary / Developer documentation / C#

CancellationToken

CancellationToken provides a standard, cooperative way to signal that an asynchronous operation should stop before it completes naturally. A CancellationTokenSource creates the token and controls when cancellation is requested; the token itself is passed into async methods, which check it periodically (or pass it to another cancellable operation) and can throw an OperationCanceledException to unwind cleanly.

Syntax

CancellationTokenSource cts = new CancellationTokenSource();\nawait SomeAsyncMethod(cts.Token);

Examples

Basic Cancellation

Creating a token source and cancelling an in-progress operation.

async Task CountAsync(CancellationToken token)
{
    for (int i = 1; i <= 10; i++)
    {
        token.ThrowIfCancellationRequested();
        Console.WriteLine(i);
        await Task.Delay(200, token);
    }
}

CancellationTokenSource cts = new CancellationTokenSource();

Task countTask = CountAsync(cts.Token);
await Task.Delay(500);
cts.Cancel();  // request cancellation after 500ms

try
{
    await countTask;
}
catch (OperationCanceledException)
{
    Console.WriteLine("Counting was cancelled");
}

Cancellation with a Timeout

A very common pattern: automatically cancel an operation if it takes too long.

async Task<string> FetchDataAsync(CancellationToken token)
{
    await Task.Delay(3000, token);  // simulate a slow operation
    return "data";
}

using CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(1));  // auto-cancel after 1 second

try
{
    string result = await FetchDataAsync(cts.Token);
    Console.WriteLine(result);
}
catch (OperationCanceledException)
{
    Console.WriteLine("Request timed out");
}

Checking IsCancellationRequested Manually

For tight loops without a natural await point, checking the flag directly can be more efficient than throwing.

void ProcessItems(List<int> items, CancellationToken token)
{
    foreach (int item in items)
    {
        if (token.IsCancellationRequested)
        {
            Console.WriteLine("Stopping early due to cancellation");
            return;
        }
        Console.WriteLine($"Processing {item}");
    }
}

Best practices

  • Accept a CancellationToken as the last parameter of any async method that might run for a while, so callers can opt into cancellation support
  • Pass the token through to every nested async call (Task.Delay(ms, token), HttpClient calls, etc.) so cancellation actually propagates, not just at the top level
  • Use CancelAfter() for straightforward timeout scenarios instead of manually managing a separate timer
  • Always dispose a CancellationTokenSource (via using) once you are done with it, to release its underlying resources

At a glance

Purpose
Applications on the .NET platform
File extension
.cs
Runs in
.NET runtime
Usually used with
.NET SDK and libraries

Specifications & further reading

Related C# documentation

async and await
The async and await keywords let you write asynchronous code that reads almost like ordinary sequential code. Marking a method async allows it to use await, which pauses execution at that point until the awaited operation completes, without blocking the calling thread - freeing it up to do other work in the meantime. This is essential for keeping applications responsive during I/O-bound work like network calls, file access, or database queries.
Task & Task<T>
Task represents an asynchronous operation that may still be running, and Task<T> represents one that will eventually produce a value of type T. Tasks are the building block that async/await is built on top of - an async method actually returns a Task (or Task<T>) that the caller can await, check the status of, or attach continuations to.
Task.WhenAll & Task.WhenAny
Task.WhenAll() lets you run multiple independent asynchronous operations concurrently and await all of them together, completing once every task finishes - much faster than awaiting them one at a time. Task.WhenAny() completes as soon as the first of several tasks finishes, useful for timeout patterns or taking whichever result arrives first.
Async Pitfalls: void vs Task, ConfigureAwait
A few recurring mistakes trip up many C# developers new to async: using async void instead of async Task (which makes exceptions impossible to catch normally), and blocking on async code with .Result or .Wait() (which can cause a deadlock in contexts with a synchronization context, like older ASP.NET or WPF apps). Understanding these pitfalls up front avoids some of the most common async-related bugs.