This recipe is about implementing the cancellation process for task-based asynchronous operations. We will learn how to use the cancellation token properly for tasks, and how to find out whether a task is cancelled before it was actually run.
using System;
using System.Threading;
using System.Threading.Tasks;
//
// Implementing a Cancellation Option
//
// This recipe is about implementing the cancellation process for task-based
// asynchronous operations. We will learn how to use the cancellation token
// properly for tasks, and how to find out whether a task is cancelled before it
// was actually run.
//
// How it works...
//
// This is another very simple example of how to implement the cancellation
// option for a TPL task, as you are already familiar with the cancellation
// token concept we discussed in Chapter 3, Using a Thread Pool.
//
// First, let's look closely at the longTask creation code. We'll pass a
// cancellation token to the underlying task once and then to the task
// constructor the second time. Why do we need to supply this token twice?
//
// The answer is that if we cancel the task before it was actually started, its
// TPL infrastructure is responsible for dealing with the cancellation, because
// our code will not execute at all. We know that the first task was canceled by
// getting its status. If we will try to call the Start method on this task, we
// will get InvalidOperationException.
//
// Then, we deal with the cancellation process from our own code. This means
// that we are now fully responsible for the cancellation process, and after we
// canceled the task, its status is still RanToCompletion, because from TPL's
// perspective, the task finished its job normally. It is very important to
// distinguish these two situations and understand the responsibility difference
// in each case.
//
namespace Chapter4.Recipe6
{
class Program
{
private static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
Console.WriteLine(longTask.Status);
cts.Cancel();
Console.WriteLine(longTask.Status);
Console.WriteLine("First task has been cancelled before execution");
cts = new CancellationTokenSource();
longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
longTask.Start();
for (int i = 0; i < 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine(longTask.Status);
}
cts.Cancel();
for (int i = 0; i < 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine(longTask.Status);
}
Console.WriteLine("A task has been completed with result {0}.", longTask.Result);
}
private static int TaskMethod(string name, int seconds, CancellationToken token)
{
Console.WriteLine(
"Task {0} is running on a thread id {1}. Is thread pool thread: {2}",
name,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
for (int i = 0; i < seconds; i ++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
if (token.IsCancellationRequested)
{
return -1;
}
}
return 42 * seconds;
}
}
}