In today's short post, I will show you how to cancel a Task with multiple inputs (aka multiple CancellationToken
s) using CancellationTokenSource
and CancellationTokenSource.CreateLinkedTokenSource
.
Problem
Imagine you have a long-running task you want to shutdown gracefully (or not) and have to listen to multiple sources of cancellation. That can be common in BackgroundService
or IHostedService
implementations where you want to listen to the IHostApplicationLifetime.ApplicationStopping
event and also to some other event or condition.
// Triggered when the application host is starting a graceful shutdown.
var hostToken = IHostApplicationLifetime.ApplicationStopping;
// Some other source for cancellation passed in by the method
var otherToken = ...;
Now you want to create a CancellationToken
that will be triggered when either of the tokens is cancelled. But a method normally only accepts a single token:
await Task.Delay(Timeout.Infinite, token); // You can't pass two tokens here
The solution: CancellationTokenSource.CreateLinkedTokenSource
. It creates a new CancellationTokenSource
that will be canceled when any of the linked tokens is canceled. You can pass in as many tokens as you want and it returns a new CancellationTokenSource
, which you can use to create a CancellationToken
for your task.
using var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(hostToken, otherToken);
var linkedToken = linkedTokenSource.Token;
await Task.Delay(Timeout.Infinite, linkedToken); // Now you can pass the linked token
Be aware that CancellationTokenSource
is an IDisposable
and should be disposed of when you are done with it. The using
statement is a good way to ensure that.
You can also cancel the linked token source manually to indicate that the task should be canceled. The "source" tokens will stay unaffected, only the linked token will be canceled.
using var t1 = new CancellationTokenSource();
using var t2 = new CancellationTokenSource();
using var t3 = CancellationTokenSource.CreateLinkedTokenSource(t1.Token, t2.Token);
t3.Cancel();
Console.WriteLine(t1.IsCancellationRequested); // False
Console.WriteLine(t2.IsCancellationRequested); // False
Console.WriteLine(t3.IsCancellationRequested); // True