C# tutorials > Asynchronous Programming > Async and Await > What are cancellation tokens (`CancellationToken`)?

What are cancellation tokens (`CancellationToken`)?

In asynchronous programming in C#, CancellationToken plays a crucial role in managing and controlling the execution of long-running operations. It allows you to gracefully stop or cancel an operation before it completes, which is particularly important in scenarios where resources need to be released or the user has requested the operation to be terminated. Understanding and using CancellationToken effectively can significantly improve the responsiveness and reliability of your applications.

Introduction to Cancellation Tokens

A CancellationToken is a structure that represents a request to cancel an operation. It's part of the System.Threading namespace. It doesn't inherently cancel anything itself; rather, it's a signal that cooperative code can listen for to know when it should stop processing. A CancellationToken is obtained from a CancellationTokenSource.

Creating a CancellationTokenSource and CancellationToken

This code demonstrates how to create a CancellationTokenSource, obtain a CancellationToken, and use it to monitor for cancellation requests within an asynchronous method (LongRunningOperationAsync). The cts.Cancel() method (which isn't called in this specific example but would be called elsewhere in your code, likely triggered by user input or another event) would signal the CancellationToken. The LongRunningOperationAsync method periodically checks cancellationToken.IsCancellationRequested and throws an OperationCanceledException if cancellation has been requested.

using System;
using System.Threading;
using System.Threading.Tasks;

public class CancellationExample
{
    public static async Task Main(string[] args)
    {
        // Create a CancellationTokenSource
        CancellationTokenSource cts = new CancellationTokenSource();

        // Get the CancellationToken from the source
        CancellationToken token = cts.Token;

        try
        {
            // Start a long-running task
            await LongRunningOperationAsync(token);
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Operation cancelled.");
        }
        finally
        {
            cts.Dispose(); // Dispose of the CancellationTokenSource when done
        }

        Console.WriteLine("Program finished.");
    }

    public static async Task LongRunningOperationAsync(CancellationToken cancellationToken)
    {
        for (int i = 0; i < 100; i++)
        {
            // Check if cancellation has been requested
            if (cancellationToken.IsCancellationRequested)
            {
                cancellationToken.ThrowIfCancellationRequested(); // Throw an exception to stop the operation
            }

            Console.WriteLine($"Processing: {i}");
            await Task.Delay(50, cancellationToken); // Simulate work and check for cancellation
        }

        Console.WriteLine("Operation completed successfully.");
    }
}

Concepts Behind the Snippet

  • CancellationTokenSource: Manages the lifecycle of the CancellationToken. It allows you to signal cancellation to the token.
  • CancellationToken: A lightweight structure that represents the cancellation signal. It doesn't perform the cancellation itself, but rather informs the code that cancellation has been requested.
  • IsCancellationRequested: A property of CancellationToken that indicates whether cancellation has been requested.
  • ThrowIfCancellationRequested(): A method of CancellationToken that throws an OperationCanceledException if cancellation has been requested. This allows you to easily stop the operation and handle the cancellation in a try-catch block.

Real-Life Use Case

Imagine a user initiating a search query on a website. If the user decides to cancel the search or the network connection is lost, you want to stop the search operation gracefully. A CancellationToken can be used to signal the cancellation to the search function, preventing unnecessary resource consumption and improving user experience. Another example is downloading a large file. If the user cancels the download, the CancellationToken allows you to stop the process cleanly, preventing corrupted files and freeing up bandwidth.

Best Practices

  • Always check IsCancellationRequested frequently: This ensures a timely response to cancellation requests.
  • Dispose of CancellationTokenSource when done: This releases resources associated with the source. Use a using statement or a finally block.
  • Pass the CancellationToken to methods that support cancellation: Many built-in asynchronous methods in .NET accept a CancellationToken parameter. Use it!
  • Handle OperationCanceledException gracefully: Ensure your application handles the exception and performs any necessary cleanup.
  • Avoid blocking operations while waiting for cancellation: Use asynchronous operations with the token to avoid blocking the thread.

Interview Tip

When discussing CancellationToken in an interview, highlight its role in cooperative cancellation. Emphasize that the token itself doesn't force cancellation; rather, it's a signal that the code must actively listen for. Explain the importance of checking IsCancellationRequested regularly and handling the OperationCanceledException. Be prepared to discuss real-world scenarios where cancellation is crucial, such as long-running tasks, network operations, or UI updates.

When to use them

Use CancellationToken whenever you have long-running operations that could benefit from being cancelled prematurely. Common scenarios include:

  • Long-running calculations
  • Network requests
  • File operations
  • UI updates
  • Background tasks

If an operation is quick and trivial, the overhead of cancellation token management may not be worth it.

Memory footprint

A CancellationToken itself is a struct, meaning it's a value type and has a relatively small memory footprint. The CancellationTokenSource, however, is a class (reference type) and consumes more memory, especially if many tokens are derived from it. Remember to dispose of the CancellationTokenSource when it's no longer needed to release these resources. Avoid creating excessive numbers of CancellationTokenSource objects unnecessarily.

Alternatives

While CancellationToken is the standard way to handle cancellation in .NET, there are alternative approaches:

  • Boolean flags: You could use a simple boolean variable to signal cancellation. However, this approach is less robust and doesn't integrate well with asynchronous methods that accept a CancellationToken. It also lacks the structured exception handling provided by CancellationToken.
  • Timeouts: Instead of explicitly cancelling an operation, you can set a timeout to automatically terminate it if it takes too long. This is useful when you want to prevent operations from running indefinitely but don't need explicit user cancellation.

CancellationToken is generally preferred for its flexibility, integration with asynchronous programming, and structured exception handling.

Pros

  • Cooperative cancellation: Allows code to gracefully stop when cancellation is requested.
  • Integration with asynchronous methods: Many built-in .NET asynchronous methods accept a CancellationToken, making it easy to integrate with existing code.
  • Structured exception handling: The OperationCanceledException provides a standard way to handle cancellation.
  • Flexibility: Allows for cancellation based on various triggers, such as user input, timeouts, or other events.

Cons

  • Requires cooperative code: The code must actively check for cancellation requests and respond accordingly. It won't automatically stop a running operation.
  • Overhead: There is some performance overhead associated with checking IsCancellationRequested, although it's generally minimal.
  • Complexity: Using cancellation tokens adds some complexity to the code, especially in more complex scenarios.

FAQ

  • What happens if I don't check `IsCancellationRequested`?

    If you don't check IsCancellationRequested, your operation will continue to run even after cancellation has been requested. The CancellationToken only signals the request; it doesn't force the operation to stop. This can lead to wasted resources and unexpected behavior.
  • Can I reuse a `CancellationToken`?

    No, once a CancellationToken has been cancelled, it cannot be reset or reused. You need to create a new CancellationTokenSource and CancellationToken for each operation that requires cancellation.
  • Is `CancellationToken` thread-safe?

    Yes, CancellationToken and CancellationTokenSource are thread-safe. You can safely access and modify them from multiple threads.