Simple Cancellation Token Example

Cancellation tokens are used to stop the execution of a method when you don’t want it to complete.

There are many reasons to do this, a few that come to mind are -

  • the execution is taking too long
  • you are running two or more similar executions in parallel and you only need one to complete
  • you are about to run out of resources so you terminate some executions
  • you no longer need the result of the execution

I’m not going to go into the details of how cancellation works, you can read about that here.

But generally speaking, the usage follows this pattern -

  1. invoke a method, passing in the cancellation token
  2. inside that method, periodically check if the cancellation token has been canceled
  3. cancel the token when you want the method to stop executing
  4. if the token has been canceled, exit the method, maybe doing some cleanup

The below code shows a simple example of how to use cancellation tokens. I start two tasks, the first does some fake work, and the second sends a cancellation request after a delay.

You can play with how long the first task takes to complete and how long the second task waits before sending the cancellation request.

Here is what is going on in the code -

  • Lines 1 and 2 set up the cancellation token source and the token itself
  • Lines 5-8 create two tasks that will run in parallel
  • Line 7 starts the fake work task, it will run for 5 seconds. It is passed the cancellation token
  • Line 8 starts the task that will send a cancellation request after 3 seconds, it is passed the cancellation token source
  • Line 11 waits for the first task to complete, in this example, it will be SendCancellationAfterDelay
  • Line 15 is only there so the program doesn’t exit before we see the output of the second task to complete, you wouldn’t do this in a real scenario
 1var cts = new CancellationTokenSource();
 2var token = cts.Token;
 3
 4Console.WriteLine("Calling DoSomeWork and SendCancellationAfterDelay");
 5var tasks = new Task[]
 6{ 
 7    DoSomeWork(5, token), // will try to run for 5 seconds
 8    SendCancellationAfterDelay(3, cts) // will send cancellation request after 3 seconds
 9};
10
11var result = await Task.WhenAny(tasks);
12
13Console.WriteLine($"Task that completed: {ParseTaskMethodName(result)}");
14
15await Task.WhenAll(tasks); // FOR DEMO ONLY. This keeps the program running so you can see the output of the second task to complete
16
17async Task DoSomeWork(int maxLoops, CancellationToken token)
18{
19    Console.WriteLine($"DoSomeWork called, maxLoops of {maxLoops}");
20    int counter = 1;
21    for (; counter <= maxLoops; counter++)
22    {
23        if(token.IsCancellationRequested)
24        {
25            Console.WriteLine($"Cancellation requested, exiting method, counter {counter}");
26            return;
27        }
28        Console.WriteLine($"MaxLoops {maxLoops}, counter {counter}");
29        await Task.Delay(1000);
30    }
31    Console.WriteLine($"Out of while loop. maxLoops {maxLoops}, counter {counter}, cancellation requested {token.IsCancellationRequested}");
32}
33
34async Task SendCancellationAfterDelay(int delay, CancellationTokenSource cts)
35{
36    Console.WriteLine($"SendCancellationAfterDelay called. Will send cancellation request after delaying {delay} seconds");
37    await Task.Delay(delay*1000);
38    Console.WriteLine($"Sent cancellation request");
39    cts.Cancel();
40}
41
42string ParseTaskMethodName(Task task)
43{
44    string taskNameFull = task.ToString();
45    int start = taskNameFull.IndexOf("__")+2;
46    int end = taskNameFull.IndexOf("|");
47    int length = end - start;
48    string taskName = taskNameFull.Substring(start, length);
49    return taskName;
50}

In a follow up post I will show how to use cancellation tokens with async methods that return a value.

comments powered by Disqus

Related