C# > Asynchronous Programming > Parallel Programming > CancellationToken

Asynchronous Task with CancellationToken

This snippet demonstrates how to perform an asynchronous task and gracefully handle cancellation requests using CancellationToken in C#. It creates a simple task that simulates work, checks for cancellation requests during its execution, and stops processing if cancellation is requested.

Code Snippet

This code defines an asynchronous method `LongRunningTask` that simulates a long-running operation. It takes a `CancellationToken` as input. Inside the loop, it checks `cancellationToken.IsCancellationRequested` to see if a cancellation has been requested. If so, it stops the task execution. The `Main` method creates a `CancellationTokenSource`, retrieves its token, starts the task, waits briefly, cancels the task using `cts.Cancel()`, and then awaits the task's completion. A `try-catch` block handles the `TaskCanceledException` that is thrown when a task is cancelled.

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

public class CancellationExample
{
    public static async Task LongRunningTask(CancellationToken cancellationToken)
    {
        Console.WriteLine("Starting long-running task...");

        for (int i = 0; i < 100; i++)
        {
            // Check for cancellation request
            if (cancellationToken.IsCancellationRequested)
            {
                Console.WriteLine("Task cancellation requested. Exiting...");
                return; // Stop execution
            }

            // Simulate some work
            Console.WriteLine($"Task running... {i}%");
            await Task.Delay(50, cancellationToken); // Asynchronously wait for 50ms
        }

        Console.WriteLine("Long-running task completed.");
    }

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

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

        // Start the long-running task
        Task task = LongRunningTask(token);

        // Allow the task to run for a bit, then cancel it
        await Task.Delay(200);
        Console.WriteLine("Requesting cancellation...");
        cts.Cancel();

        // Wait for the task to complete (or be cancelled)
        try
        {
            await task;
        }
        catch (TaskCanceledException)
        {
            Console.WriteLine("Task was cancelled.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Exception: {ex.Message}");
        }

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

Concepts Behind the Snippet

This snippet uses the following concepts:

  • Asynchronous Programming: Allows performing long operations without blocking the main thread.
  • Tasks: Represent an asynchronous operation.
  • CancellationToken: Used to signal that an operation should be cancelled.
  • CancellationTokenSource: Creates and manages `CancellationToken` instances.

Real-Life Use Case

Consider a file download operation in a web application. The download task can be cancelled if the user closes the browser or navigates away from the page. A `CancellationToken` allows the server to stop the download and free up resources.

Best Practices

  • Always check cancellationToken.IsCancellationRequested frequently in long-running operations.
  • Pass the CancellationToken to asynchronous methods that support cancellation.
  • Handle the TaskCanceledException properly to ensure resources are released.
  • Use cancellationToken.ThrowIfCancellationRequested() to throw an exception immediately when cancellation is requested.

When to Use Them

Use CancellationToken when you have a long-running operation and need to provide the user (or another part of the system) with the ability to stop it prematurely. This is particularly useful in GUI applications, web servers, and services where responsiveness is crucial.

Memory Footprint

The memory footprint of using `CancellationToken` is relatively small. A `CancellationTokenSource` and its associated `CancellationToken` consume a few bytes of memory. The overhead is minimal compared to the resources consumed by the long-running operation itself.

Alternatives

Alternatives to using `CancellationToken` for stopping asynchronous operations include:

  • Flags: Using a boolean flag to indicate cancellation. However, this approach requires manual checking and synchronization, which can be error-prone.
  • Timeouts: Setting a timeout for the operation. This is useful when you want to limit the execution time, but it doesn't provide a way for external code to request cancellation.

`CancellationToken` provides a cleaner and more robust solution for managing cancellation requests.

Pros

  • Clean Abstraction: Provides a clear and standardized way to handle cancellation.
  • Thread-Safe: `CancellationToken` and `CancellationTokenSource` are thread-safe.
  • Integration: Integrates well with asynchronous programming constructs like `Task` and `async/await`.

Cons

  • Requires Cooperation: The operation being cancelled must actively check the IsCancellationRequested property.
  • Exception Handling: Requires proper exception handling for TaskCanceledException.

FAQ

  • What happens if I don't check for cancellation?

    If you don't check for cancellation, the long-running operation will continue to execute even if cancellation is requested. This can lead to wasted resources and potentially incorrect results.
  • Can I reuse a CancellationToken?

    Yes, you can pass the same `CancellationToken` to multiple tasks. When the token is cancelled, all tasks using it will be notified.
  • What's the difference between Cancel() and CancelAfter()?

    `Cancel()` immediately requests cancellation. `CancelAfter(delay)` requests cancellation after a specified delay.