C# CancellationTokenSource

Summary: in this tutorial, you’ll learn how to use C# CancellationTokenSource to cancel an asynchronous operation.

Cancellation is cooperative

In .NET, cancellation is cooperative. It means that when you request to cancel an asynchronous operation, it’s up to the code running that operation to check whether cancellation has been requested and stop running if it has.

Also, cancellation is not something that is automatically enforced by .NET, but rather a mechanism that you have to implement manually in your code using a CancellationToken object.

In other words, if you want an asynchronous operation to be cancellable, in the operation, you need to explicitly check for cancellation requests using the IsCancellationRequested property of the CancellationToken object, and stop running if it returns true.

To create an CancellationToken, you use the CancellationTokenSource class.

How to use CancellationTokenSource class

Here are the steps for using the CancellationTokenSource class:

First, create a new CancellationTokenSource object that can be used to generate a cancellation token:

var cts = new CancellationTokenSource();Code language: C# (cs)

Second, pass the cancellation token to an asynchronous operation:

AsyncOperation(cts.Token);Code language: C# (cs)

Third, check for cancellation requests in the asynchronous operation and stop running if the cancellation has been requested:

Task<T> AsyncOperation(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        // Do some work...
    }

    // The operation was cancelled...
}Code language: C# (cs)

Finally, signal cancellation using the Cancel() method of the CancellationTokenSource object:

cts.Cancel();Code language: C# (cs)

C# CancellationTokenSource example

using static System.Console;

static List<int> Square(int max, CancellationToken token)
{
    var result = new List<int>();
    for (int i = 0; i < max; i++)
    {
        Thread.Sleep(500);

        if (token.IsCancellationRequested)
        {
            WriteLine("Cancellation requested!!!");
            token.ThrowIfCancellationRequested();
            break;
        }

        var square = i * i;

        WriteLine(square);
        result.Add(square);
    }
    return result;
}

CancellationTokenSource cts = new();

var task = Task.Run(() => Square(10, cts.Token));
task.ContinueWith((t) =>
{
    var squares = t.Result;
    WriteLine("The square numbers are");
    foreach (var square in squares)
    {
        Write($"{square} ");
    }
}, TaskContinuationOptions.OnlyOnRanToCompletion);


WriteLine("Press 'c' to cancel.");
while (true)
{
    var keyInfo = ReadKey(true);
    if (keyInfo.KeyChar == 'c')
    {
        if (task.Status == TaskStatus.Running)
        {
            cts.Cancel();
            WriteLine("Canceling the task...");
            break;
        }
    }
    // quit if the task has been completed
    if (task.Status == TaskStatus.RanToCompletion)
    {
        break;
    }
}
Code language: C# (cs)

Output:

Press 'c' to cancel.
0
1
4
9
16
25
Canceling the task...Code language: plaintext (plaintext)

How it works.

First, create a CancellationTokenSource object.

CancellationTokenSource cts = new();Code language: C# (cs)

Second, create a new task that executes the Square() method:

var task = Task.Run(() => Square(10, cts.Token));Code language: C# (cs)

The Square() method returns a list of square numbers based on the argument. For example, if you pass 10 to the Square() method, it’ll return a list of square numbers of 1, 2, 3, … 9, which are 2, 4, 9, … 81. Also, we pass a CancellationToken to the Square() method.

Third, define the Square() method that returns a list of square numbers. The Square() method uses the CancellationToken object to check if the cancellation has been requested. If yes, it calls token.ThrowIfCancellationRequested() method to raise an OperationCanceledException and exit the loop.

Fourth, create a continuation that runs only if the task is running to the completion:

task.ContinueWith((t) =>
{
    var squares = t.Result;
    WriteLine("The square numbers are");
    foreach (var square in squares)
    {
        Write($"{square} ");
    }
}, TaskContinuationOptions.OnlyOnRanToCompletion);Code language: C# (cs)

Fifth, create a loop that checks if the user presses the letter c and sends a cancellation using the Cancel() method of the CancellationTokenSource object and exit the loop. Also, if the task status is RanToCompletion, then exit the loop:

WriteLine("Press 'c' to cancel.");
while (true)
{
    var keyInfo = ReadKey(true);
    if (keyInfo.KeyChar == 'c')
    {
        if (task.Status == TaskStatus.Running)
        {
            cts.Cancel();
            WriteLine("Canceling the task...");
            break;
        }
    }
    // quit if the task has been completed
    if (task.Status == TaskStatus.RanToCompletion)
    {
        break;
    }
}Code language: C# (cs)

Summary

  • Use C# CancellationTokenSource to cancel an asynchronous operation executed by a task.
Was this tutorial helpful ?