The System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> classes support cancellation through the use of cancellation tokens. But what is the right way of doing this? The Task.Run API takes a cancellation token, but is this enough? If we want to actually end a task when we attempt a cancellation, how do we do it?
Let's compare.... We'll try one task passing a token into just Task.Run. We'll try another task that also handles the token in the delegate. THen we'll try a task that only handles the token in the delegate.
Let's try the following code:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Practice
{
class Program
{
/// <summary>
/// The main method
/// </summary>
/// <param name="args">Arguments not used for example</param>
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(2));
var token = cts.Token;
// Try a task just passing the token
_ = Task.Run(() =>
{
for (int i = 1; i <= 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.WriteLine(i + " seconds passed in thread 1");
}
}, token);
// Try a task in which the delegate handles the token, and the token is passed
_ = Task.Run(() =>
{
for (int i = 1; i <= 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.WriteLine(i + " seconds passed in thread 2");
if (token.IsCancellationRequested) { return; }
}
}, token);
// Try a task in which only the delegate handles the token
_ = Task.Run(() =>
{
for (int i = 1; i <= 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
Console.WriteLine(i + " seconds passed in thread 3");
if (token.IsCancellationRequested) { return; }
}
});
Console.WriteLine("");
Console.WriteLine("Press any key to exit...");
Console.WriteLine("");
Console.ReadKey();
}
}
}
We would expect Task 1 to run to completion.
Task 2 will end when canceled.
What about Task 3?
The results at the console are:
Task 1 ran to completion. Task 2 ended when canceled. Task 3 ended when canceled.
So what does this tell us? A token must be used in the delegate of a task body in order to be effective. A token passed into Task.Run only will prevent the task from running if it has not run yet and is still scheduled to run.