Waiting for long running tasks to be cancelled.

How to await a Cancellation Token in C#

Cillié Malan
3 min readNov 18, 2019

Sometimes you want to let something run until it is cancelled. This is an elegant way to do this in C#.

One of the features of .Net asynchronous programming is the support for task cancellation by using a Cancellation Token. I would wager that in most of your day-to-day programming cancellation is the exception and not the rule. So the use of a cancellation token is usually like this:

if (token.IsCancellationRequested)
{
break;
}

Or this:

token.ThrowIfCancellationRequested();

However, in some cases a task needs to run until it is explicitly cancelled.

Background Service Cancellation

One such case is in the implementation of long running background services in .Net Core.

How do we wait until cancelled?

But then in practice, how do we “wait” for a cancellation token? One option is to loop and continually ask the token if cancellation is requested yet:

What one might see in the wild.

But sometimes you have workloads that aren’t a simple loop doing the same thing over and over again. Once such case is when your background task is consuming items from a message queue. The task must run until cancellation, after which something is disposed and the message queuing system will gracefully requeue and reprocess later.

Here is one such example with a nasty confusing wait at the end:

What’s going on there at the end???

Here it does the work, and at the end it does the same looping stuff we’ve seen before.

Now there are better ways to do this, but perhaps the most intuitive thing is simply waiting for the cancellation token?

Way better!

A Cancellation Token Awaiter

Too bad we can’t do this. Or can we?

Fortunately for us the async/await system is extendable by adding an awaiter. This comes as a hint by the compile error we get when wo do try to await a cancellation token:

Oppertunity!

Here we see that the compiler is looking for something it can’t find. If we want to be able to await a cancellation token, we simply need to create this stuff and it will work!

And this is how to implement said awaiter:

And there you have it! We create an extension method like the error message suggested, which returns our awaiter. The awaiter hooks into the cancellation token callback to signal when the cancellation has happened, and when the compiler generated code tries to get the result, it throws an OperationCancelledException like it would if this had been a cancelled task.

Note: it throws an exception, and does not merely return, in order to be consistent with how the .Net framework deals with cancellation.

To use this code simply drop the file into your project and change the namespace to whatever you want.

--

--