26.5.2 取消任务
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ThreadStudy
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
Task<int> t = new Task<int>(n => Sum(cts.Token,100),cts.Token);//区别于线程池,两个参数
t.Start();
cts.Cancel();
try
{
Console.WriteLine("The sum is:" + t.Result);
}
catch (AggregateException e)
{
e.Handle(ex => ex is OperationCanceledException);
Console.WriteLine("The Task was canceled...");
}
}
static int Sum(CancellationToken ct, int num)
{
Thread.Sleep(100);
ct.ThrowIfCancellationRequested();
if (num == 0)
return 0;
else
return num + Sum(ct, num - 1);
}
}
}
The Task was canceled...
Press any key to continue . . .
在计算限制操作的循环中,我们通过调用CancellationToken的ThrowIfCancellationRequested方法定时检查操作是否已经取消。这个方法和Cancellation的IsCancelationRequested属性是相似的。然而,如果CancellationTokenSource已经取消,ThrowIfCancellationRequested会抛出一个OperationCanceledException。之所以选择抛出一个异常,是因为有别于由ThreadPool会抛出一个的QueueUserWorkItem方法所初始化的工作项(work
item),任务有表示已完成的方法,任务甚至还能返回一个值。所以,需要采取一种方法将已完成的任务和出错的任务区分开。而让任务抛出一个异常,就可以知道任务没有一直运行到结束。
创建一个Task时,可以将一个CancellationToken传给Task的构造器,从而将这个CancellationToken和该Task关联起来。如果CancellationToken在Task调度前取消,Task会被取消,永远都不会执行。但是,如果Task已经调度(通过调用Start方法),那么Task为了允许它的操作在执行期间取消,Task的代码就必须显式支持取消。遗憾的是,虽然Task对象关联了一个CancellationToken,但却没有办法访问它。因此,必须通过某种方式,在Task的代码本身中获得用于创建Task对象的同一个CancellationToken。为了写这样的代码,最简单的办法就是使用一个Lambda表达式,并将CancellationToken作为一个闭包变量"传递"。