1、需求
我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗? 当然我们可以做到。
在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的时候传入此参数,就可以将主线程和任务相关联,然后在任务中设置“取消信号“叫做ThrowIfCancellationRequested来等待主线程使用Cancel来通知,一旦cancel被调用。task将会抛出OperationCanceledException来中断此任务的执行,最后将当前task的Status的IsCanceled属性设为true。
注意:一定要处理这个异常,可以通过调用Task.Result成员来获取这个异常。如果一直不查询Task的Exception属性。你的代码就永远注意不到这个异常的发生,如果不能捕捉到这个异常,垃圾回收时,抛出AggregateException,进程就会立即终止,这就是“牵一发动全身”,莫名其妙程序就自己关掉了,谁也不知道这是什么情况。所以,必须调用前面提到的某个成员,确保代码注意到异常,并从异常中恢复。因此可以将条用Task的某个成员来检查Task是否跑出了异常,通常调用Task的Result。下面看代码:
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
Task task1 = new Task(() => { Run1(ct); }, ct);
Task task2 = new Task(Run2);
try
{
task1.Start();
task2.Start();
//在这段时间内,ct.ThrowIfCancellationRequested();是不会被触发的
Thread.Sleep(1000);
cts.Cancel();
//这时候会触发ct.ThrowIfCancellationRequested();
Task.WaitAll(task1, task2);
}
catch (AggregateException ex)
{
foreach (var e in ex.InnerExceptions)
{
Console.WriteLine("\nhi,我是OperationCanceledException:{0}\n", e.Message);
}
//task1是否取消
//Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled);
//Console.WriteLine("task2是不是被取消了? {0}", task2.IsCanceled);
}
Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled);
Console.WriteLine("task2是不是被取消了? {0}", task2.IsCanceled);
Console.Read();
}
static void Run1(CancellationToken ct)
{
//下面这句话是没有用的,因为这个时候没有收到Cancel的消息
ct.ThrowIfCancellationRequested();
Console.WriteLine("我是任务1");
Thread.Sleep(2000);//是为了在Cancel的时候Run1没有执行完
//当上面等到1000ms多的时候实际上已经接收到Cancel的消息了,但是这个时候不会取消task的,只有调用下面这句话的时候才会取消task
ct.ThrowIfCancellationRequested();
Console.WriteLine("我是任务1的第二部分信息");
}
static void Run2()
{
Console.WriteLine("我是任务2");
}
注意:
1、只有ct.ThrowIfCancellationRequested();才能真正的取消Task,此外,ct.ThrowIfCancellationRequested();的调用必须是接收到Cancel命令过来才会触发的。
2、个人认为:这样子的目的是为了接收到取消的时候,不会立马停止Task,而是只有在合适的时间停止。
2、Task的写法:
var cts = new CancellationTokenSource();
var ct = cts.Token;
Task task1 = new Task(() => { Run1(ct); }, ct);