Throw ThrowIfCancellationRequested in a Method that Returns a Value when Using a Cancellation Token

Download full source code.

Say you have a method that returns an int, but you want to be able to cancel it via a cancellation token, the method will not complete but all paths must return some int. One option is to return an int after canceling, knowing it won’t be used.

Below is an example where you return -1 if the method is canceled. Even though the caller will never use the result, it is still required to return an int. I don’t like what this code expresses to the reader.

 1internal async Task<int> DoSomeWork(int someInput, CancellationToken token)
 2{
 3    while (something == true)
 4    {
 5        if (token.IsCancellationRequested)
 6        {
 7            return -1; // you want to stop the method, but you still need to return an int
 8        }
 9        // do some work
10    }
11    return result; // the real result if the method is allowed to complete
12}

Another option is to throw an exception if the cancel request is made. Other people might not like this because throwing an exception can be expensive.

 1internal async Task<int> DoSomeWork(int someInput, CancellationToken token)
 2{
 3    while (something == true)
 4    {
 5        if (token.IsCancellationRequested)
 6        {
 7            token.ThrowIfCancellationRequested();
 8        }
 9        // do some work
10    }
11    return result; // the real result if the method is allowed to complete
12}

Below is how to call the method, and invoke the cancellation. You don’t necessarily need to handle the exception, in the example below it definitely is not required as I already have the result from the first call, but I included it for the sake of this post.

The first few lines are pretty straightforward, setting up the cancellation token, invoking the methods, and waiting for the first task to complete.

Then online line 14 I send the cancellation request. Lines 16-20 demonstrate how to find which task was cancelled, and how to handle the exception.

There probably are not very many scenarios where you would want to do this, but it might help you someday.

 1var cts = new CancellationTokenSource();
 2var token = cts.Token;
 3
 4Worker worker = new Worker();
 5var tasks = new Task<int>[]
 6{
 7    worker.DoSomeWork(2, token), //this will complete first
 8    worker.DoSomeOtherWork(4, token) //this will be cancelled after the first one completes
 9};
10
11var completedTask = await Task.WhenAny(tasks); //first task completes
12Console.WriteLine($"Completed first task {completedTask.MethodName()}, result = {await completedTask}");
13
14cts.Cancel(); //cancel the other task
15
16foreach (var task in tasks)
17{
18    try 
19    {
20        Console.WriteLine($"{task.MethodName()}. Id {task.Id}, result {await task}, status {task.Status}");
21    }
22    catch(OperationCanceledException ex)
23    {
24        Console.WriteLine($"{task.MethodName()}. Id {task.Id}, status {task.Status}, {ex.Message}");
25    }
26}

That’s it, hope it is helpful some someone.

Download full source code.

comments powered by Disqus

Related