Cancellation Token in C# - Explained

Dec 15, 2023·

4 min read

Have you come across any scenario where you started long-running asynchronous tasks in C# but want to cancel in the middle of the execution?

Either the user wants to cancel or the application itself is required to cancel the operation, it is a good idea to cancel the resource-heavy long-running operations (logic built using a database, report generation, etc.) gracefully.

Let's discuss about cancellation token in c# which is responsible for canceling tasks.

What is the Cancellation token in C#?

A cancellation token in C# is represented by the CancellationToken struct is a way to notify an operation or task that should be cancelled cooperatively.

A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects.

Why do you need a Cancellation token?

To enable cooperative cancellation of tasks/operations that might be no longer needed or take a long time to complete.

Cooperative cancellation means that the operation can check the cancellation token periodically and gracefully stop if it has been canceled, rather than being aborted abruptly.

This allows the operation to release any resources, close any files, or perform any other cleanup before exiting.

How to create a cancellation token in C#?

You can create a cancellation token by using a CancellationTokenSource object, which manages the cancellation tokens retrieved from its CancellationTokenSource.Token property.

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

You can then pass the cancellation token to any threads, tasks, or operations that should receive the cancellation notification.

How to cancel the asynchronous operation?

You can either use the Cancel() or CancelAfter() method from CancellationTokenSource to cancel the token.

cancellationTokenSource.Cancel(); //Cancel immediately
cancellationTokenSource.CancelAfter(1000); //Cancel after given time

If you want to cancel the asynchronous APIs, the route endpoint should use the CancellationToken delegate handler as mentioned below. Your code should check for a token.IsCancellationRequested status periodically and acts based on the true/false value.

app.MapGet("/ApiTest", async (CancellationToken token)

How do you know the operation is cancelled?

CancellationToken has a property called IsCancellationRequested returns true if the token has been cancelled.

You can also use the ThrowIfCancellationRequested method to throw an OperationCanceledException if the token has been cancelled.

How to use cancellationtoken in C#?

The asynchronous operations must periodically check for cancellationToken.IsCancellationRequested is true. If true, you should write your logic to gracefully exit all the long-running processes.

while (!cancellationToken.IsCancellationRequested)
{
    //Long running operations here. 
}

Sometimes the OperationCanceledException will be thrown, hence introduce the try-catch block to handle the exceptions.

try
{
    //Operations that might throw OperationCanceledException on cancel
}
catch (OperationCanceledException)
{
    //Handle the cancellation
}

Do the cleanup activities when cancellationToken.IsCancellationRequested true or inside catch block of OperationCanceledException. You may also use CancellationToken.Register() to register your custom cleanup logic as below, which will be executed once the token is cancelled.

WebClient webClient= new WebClient();
cancellationToken.Register(() =>
{
   webClient.CancelAsync();
});

Cancellationtoken example in c# (using WebAPI)

app.MapGet("/ApiTest", async (CancellationToken token) =>
{
    var result = await DoWorkAsync(token);
    return Results.Ok(result);
})
.WithName("ApiTest")
.WithOpenApi();

static async Task<string> DoWorkAsync(CancellationToken cancellationToken)
{
    try
    {
        // A long-running operation like database, business logics
        await Task.Delay(5000, cancellationToken);
        return "Operation completed.";
    }
    catch (OperationCanceledException ex)
    {
        return "Operation canceled. (No output to client)";
    }
}
  • If the above API is executed without cancellation, it will return the "Operation completed." message.

  • If the above API is cancelled, it will cancel the operation from DoWorkAsync method as well. Please note that no output will be returned to the client upon cancellation.

Cancellationtoken example using console app

using System;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        // Create a cancellation token source
        CancellationTokenSource cts = new CancellationTokenSource();

        // Pass the cancellation token to an asynchronous method
        DoWorkAsync(cts.Token);

        // Wait for user input to cancel the operation
        Console.WriteLine("Press any key to cancel...");
        Console.ReadKey();

        // Cancel the operation by calling Cancel() on the cancellation token source
        cts.Cancel();

        Console.WriteLine("Operation canceled.");
        Console.ReadKey();
    }

    static async void DoWorkAsync(CancellationToken cancellationToken)
    {
        try
        {
            // Simulate some long-running operation
            await Task.Delay(5000, cancellationToken);

            // Check if cancellation has been requested
            if (cancellationToken.IsCancellationRequested)
            {
                Console.WriteLine("Operation canceled.");
            }
            else
            {
                Console.WriteLine("Operation completed.");
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation canceled.");
        }
    }
}

Please note that the above code is auto-generated using AI.

General recommendations

  • Check the cancellation token periodically inside the long-running process. If cancelled, exit the process after handling necessary activities like closing database connections, releasing external resources, etc.

  • Wherever necessary, pass the cancellation token to all the asynchronous methods which can be cancelled at a later time.

  • Know when you’ve passed the point of no cancellation. Sometimes you may start changing the state of an object in the database and cancellation might lead to data inconsistency or other serious issues.

  • Don’t throw OperationCanceledException after you’ve completed the work, just because the token was signalled.

Summary

You should now understand how to use CancellationToken in C# to cancel the long-running operations when the request is cancelled. This will help us to save server resources and improve the application performance.

References

Recommended patterns for CancellationToken - Developer Support (microsoft.com)

How to Cancel a Task in C# using Cancellation Token - Dot Net Tutorials

Using a CancellationToken in .NET Core Web API | by Danielle | Medium

Why Do You Need a Cancellation Token in C# for Tasks? | HackerNoon