What is C#’s CancellationToken?

What is C#’s CancellationToken?

·

8 min read

Introduction

CancellationToken is a struct in the System.Threading namespace of the .NET Framework that is used to request cancellation of a long-running operation. It is commonly used in asynchronous programming to allow the calling code to cancel an operation that is currently in progress.

Here is an example of how to use a CancellationToken in C#:

using System.Threading;

public async Task DoWorkAsync(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        // Perform some work here...

        // Check for cancellation every iteration
        cancellationToken.ThrowIfCancellationRequested();
    }
}

In this example, the DoWorkAsync method is an async task that performs some work in a loop. The cancellationToken parameter is used to check for a cancellation request at the beginning of each loop iteration. If a cancellation request has been made, the ThrowIfCancellationRequested method is called, which throws a OperationCanceledException to signal that the operation has been cancelled.

CancellationTokenSource

CancellationTokenSource is a class in the System.Threading namespace that is used to create a CancellationToken. It has a Cancel method that can be called to request cancellation of the associated CancellationToken.

Here is an example of how to use a CancellationTokenSource to cancel a long-running operation:

using System.Threading;

public async Task DoWorkAsync()
{
    CancellationTokenSource cts = new CancellationTokenSource();

    // Start the long-running operation in a separate task
    Task workTask = Task.Run(() => DoWorkAsync(cts.Token));

    // Cancel the operation after 5 seconds
    await Task.Delay(5000);
    cts.Cancel();

    // Wait for the operation to complete
    await workTask;
}

private async Task DoWorkAsync(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        // Perform some work here...

        // Check for cancellation every iteration
        cancellationToken.ThrowIfCancellationRequested();
    }
}

In this example, the DoWorkAsync method creates a CancellationTokenSource and passes its associated CancellationToken to the DoWorkAsync method in a separate task. After a delay of 5 seconds, the Cancel method is called on the CancellationTokenSource, which sets the cancellation request on the CancellationToken. The DoWorkAsync method then checks for a cancellation request at the beginning of each loop iteration, and throws a OperationCanceledException if one has been made.

Linking CancellationTokenSources

CancellationTokenSource also has a CreateLinkedTokenSource method that allows you to link multiple CancellationTokenSources together. When any of the linked CancellationTokenSources are cancelled, all of the linked CancellationTokens will be cancelled as well.

Here is an example of how to use the CreateLinkedTokenSource method:

using System.Threading;
CancellationTokenSource cts1 = new CancellationTokenSource();
CancellationTokenSource cts2 = new CancellationTokenSource();

CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token);

// Start a long-running operation using the linked CancellationToken
Task workTask = Task.Run(() => DoWorkAsync(linkedCts.Token));

// Cancel the operation by cancelling one of the linked CancellationTokenSources
cts1.Cancel();

// Wait for the operation to complete
await workTask;

In this example, the CreateLinkedTokenSource method is used to create a linked CancellationTokenSource from cts1 and cts2. If either of these CancellationTokenSources are cancelled, the linked CancellationToken will also be cancelled.

Real-World Examples

Web Application

One real-world example of using CancellationToken in C# is in a web application that allows users to perform long-running tasks, such as importing a large amount of data or generating a report.

In this scenario, the user initiates the long-running task by making a request to the web server. The server then starts the task in a separate thread, and returns a unique identifier to the client to track the progress of the task.

The client can then make periodic requests to the server to check the status of the task, and can also request cancellation of the task by sending a cancellation request to the server with the unique identifier.

On the server side, the task would be implemented using a CancellationToken to check for a cancellation request at regular intervals. Here is an example of how this might look:

using System.Threading;
using System.Web.Mvc;

public class TaskController : Controller
{
    // Dictionary to track the status of tasks
    private static Dictionary<string, CancellationTokenSource> _tasks = new Dictionary<string, CancellationTokenSource>();

    [HttpPost]
    public ActionResult StartTask()
    {
        // Generate a unique identifier for the task
        string taskId = Guid.NewGuid().ToString();

        // Create a CancellationTokenSource for the task
        CancellationTokenSource cts = new CancellationTokenSource();

        // Add the CancellationTokenSource to the dictionary
        _tasks.Add(taskId, cts);

        // Start the task in a separate thread
        Task.Run(() => DoTaskAsync(cts.Token));

        // Return the task identifier to the client
        return Json(new { taskId = taskId });
    }

    [HttpPost]
    public ActionResult CancelTask(string taskId)
    {
        // Look up the CancellationTokenSource for the task
        CancellationTokenSource cts = _tasks[taskId];

        // Cancel the task
        cts.Cancel();

        // Remove the CancellationTokenSource from the dictionary
        _tasks.Remove(taskId);

        return Json(new { success = true });
    }

    private async Task DoTaskAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // Perform some work here...

            // Check for cancellation every iteration
            cancellationToken.ThrowIfCancellationRequested();
        }
    }
}

In this example, the StartTask action creates a CancellationTokenSource for the task and starts the task in a separate thread. The CancelTask action cancels the task by calling the Cancel method on the CancellationTokenSource, and removes the CancellationTokenSource from the dictionary. The DoTaskAsync method performs the work for the task and checks for a cancellation request at regular intervals using the CancellationToken.

This approach allows the client to cancel the long-running task at any time, and allows the server to communicate the cancellation request to the task in progress.

Console Application

Another example of using CancellationToken in C# is in a console application that performs a long-running operation, such as downloading a large file from the internet.

In this scenario, the console application can use CancellationToken and CancellationTokenSource to allow the user to cancel the operation by pressing a key on the keyboard. Here is an example of how this might look:

using System;
using System.Threading;

class Program
{
    static void Main(string[] args)
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        // Start the long-running operation in a separate task
        Task downloadTask = Task.Run(() => DownloadFileAsync(cts.Token));

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

        // Cancel the operation
        cts.Cancel();

        // Wait for the operation to complete
        downloadTask.Wait();
    }

    private static async Task DownloadFileAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // Perform some work here...

            // Check for cancellation every iteration
            cancellationToken.ThrowIfCancellationRequested();
        }
    }
}

In this example, the DownloadFileAsync method is a long-running operation that is started in a separate task. The Main method waits for the user to press a key on the keyboard, and then cancels the operation by calling the Cancel method on the CancellationTokenSource. The DownloadFileAsync method checks for a cancellation request at regular intervals using the CancellationToken.

This approach allows the user to cancel the long-running operation at any time by pressing a key on the keyboard, and allows the console application to communicate the cancellation request to the operation in progress.

WPF Application

Another example of using CancellationToken in C# is in a WPF application that performs a long-running operation, such as searching a large database.

In this scenario, the WPF application can use CancellationToken and CancellationTokenSource to allow the user to cancel the operation by clicking a cancel button on the user interface. Here is an example of how this might look:

using System.Threading;
using System.Windows;
using System.Windows.Controls;

public partial class MainWindow : Window
{
    private CancellationTokenSource _cts;

    public MainWindow()
    {
        InitializeComponent();
    }

    private void SearchButton_Click(object sender, RoutedEventArgs e)
    {
        // Disable the search button and show the cancel button
        SearchButton.IsEnabled = false;
        CancelButton.Visibility = Visibility.Visible;

        // Create a CancellationTokenSource for the search
        _cts = new CancellationTokenSource();

        // Start the search in a separate task
        Task searchTask = Task.Run(() => SearchAsync(_cts.Token));
    }

    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        // Cancel the search
        _cts.Cancel();

        // Enable the search button and hide the cancel button
        SearchButton.IsEnabled = true;
        CancelButton.Visibility = Visibility.Collapsed;
    }

    private async Task SearchAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // Perform the search here...

            // Check for cancellation every iteration
            cancellationToken.ThrowIfCancellationRequested();
        }
    }
}

In this example, the SearchButton_Click event handler starts the search operation in a separate task when the user clicks the search button. The CancelButton_Click event handler cancels the search operation by calling the Cancel method on the CancellationTokenSource, and disables the search button and hides the cancel button. The SearchAsync method performs the search and checks for a cancellation request at regular intervals using the CancellationToken.

This approach allows the user to cancel the long-running operation at any time by clicking the cancel button, and allows the WPF application to communicate the cancellation request to the operation in progress.

Desktop Application

Another example of using CancellationToken in C# is in a desktop application that performs a long-running operation, such as importing a large amount of data from a file.

In this scenario, the desktop application can use CancellationToken and CancellationTokenSource to allow the user to cancel the operation by pressing a cancel button on the user interface. Here is an example of how this might look in WPF:

using System.Threading;
using System.Windows;
using System.Windows.Controls;

public partial class MainWindow : Window
{
    private CancellationTokenSource _cts;

    public MainWindow()
    {
        InitializeComponent();

        // Create the cancel button
        Button cancelButton = new Button { Content = "Cancel" };
        cancelButton.Click += CancelButton_Click;

        // Add the cancel button to the grid
        Grid.SetRow(cancelButton, 1);
        MainGrid.Children.Add(cancelButton);
    }

    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        // Cancel the import
        _cts.Cancel();

        // Disable the cancel button
        ((Button)sender).IsEnabled = false;
    }

    private void ImportButton_Click(object sender, RoutedEventArgs e)
    {
        // Create a CancellationTokenSource for the import
        _cts = new CancellationTokenSource();

        // Start the import in a separate task
        Task importTask = Task.Run(() => ImportAsync(_cts.Token));
    }

    private async Task ImportAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // Perform the import here...

            // Check for cancellation every iteration
            cancellationToken.ThrowIfCancellationRequested();
        }
    }
}

In this example, the CancelButton_Click event handler cancels the import operation by calling the Cancel method on the CancellationTokenSource, and disables the cancel button. The ImportAsync method performs the import and checks for a cancellation request at regular intervals using the CancellationToken.

This approach allows the user to cancel the long-running operation at any time by pressing the cancel button, and allows the desktop application to communicate the cancellation request to the operation in progress.

Conclusion

CancellationToken and CancellationTokenSource are useful tools for requesting cancellation of long-running operations in C#. They allow you to cancel an operation that is currently in progress, and provide a mechanism for communicating the cancellation request to the operation itself.

I hope this article has helped you understand how to use CancellationToken and CancellationTokenSource in your C# code. Happy coding!