Perhaps you know this problem from your first steps with asynchronous functions. You want to call them, but a CancellationToken
is requested. But where does it come from if you do not have one at hand? At first glance, a quick way is to use an empty CancellationToken
. But is that really the solution?
In this blog article, I explain what CancellationToken
s are for and what the CancellationTokenSource
is for.
Let us start with a thought experiment: You have an Application
with different Worker
threads. The Application
is closed. How can you make the whole Application
with all the Worker
threads close?
One solution would be a global bool
variable “closed”, which is set in this case. All Worker
s have a loop built in, which regularly checks this bool
and terminates if it is set. Basically, the Application
and all Worker
s must cooperate and react to the signal.
class Application
{
public static bool closed = false;
Application()
{
var worker= new Worker();
worker.Run();
Thread.Sleep(2500);
closed = true;
// Wait for termination of Tasks
Thread.Sleep(2500);
}
}
class Worker
{
public async void Run()
{
bool moreToDo = true;
while (moreToDo)
{
await DoSomething();
if (Application.closed)
{
return;
}
}
}
}
This is what the CancellationToken
can be used for. The token is passed to the involved Worker
s and functions. Throught it, the status of whether or not it was cancelled can be queried. The token is provided via a CancellationTokenSource
, which owns and manages the token.
class Application
{
Application()
{
var source = new CancellationTokenSource();
var token = source.Token;
var worker = new Worker();
worker.Run(token);
Thread.Sleep(2500);
source.Cancel();
// Wait for cancellation of Tasks and dispose source
Thread.Sleep(2500);
source.Dispose();
}
}
class Worker
{
public async void Run(CancellationToken token)
{
bool moreToDo = true;
while (moreToDo)
{
await DoSomething(token);
if (token.IsCancellationRequested)
{
return;
}
}
}
}
The call to source.Cancel()
sets the token to canceled. CancellationToken
s and sources offer significantly more built-in possibilities, such as callbacks, WaitHandle, CancelAfter and many more.
So using an empty CancellationToken
is not a good solution. This means that cancellations cannot be passed on and a task could continue to run in the background even though the Application
should have been terminated a long time ago.
I hope I have been able to bring the topic a little closer to you. If you have already encountered such problems and found solutions, please leave a comment.