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.