Cancel WhenAny - linked CancellationTokenSource

28/05/2024

In today's short post, I will show you how to cancel a Task with multiple inputs (aka multiple CancellationTokens) 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

Task.WhenAny - How to cancel other tasks

This short post will give an overview how to cancel all remaining tasks once Task.WhenAny finishes. Plus how Task.WhenAny behaves in different circumstances.

Avoid multiple boolean parameters

Boolean parameters are nice, but it's hard to keep track of what each one does when you have multiple of them. In this blog post, we will see why it's better to avoid multiple boolean parameters and how to refactor them.

5 useful extensions for Task<T> in .NET

In this short blog post, I will show you 5 useful extensions for Task in .NET. We will build them as extension methods, so there are easy to use. On top, I will show a small example of how to use them. So let's go!

An error has occurred. This application may no longer respond until reloaded. Reload x