1 private static void TaskCancelDemo() 2 { 3 //向应该被取消的 System.Threading.CancellationToken 发送信号 4 CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); 5 6 //将在线程池上运行的指定工作排队,并返回代表该工作的 Task(TResult) 对象。 借助取消标记,可取消工作。 7 //以异步方式执行的工作量。应用以取消工作的取消标记。 8 Task<int> task = Task.Run(() => Sum(cancellationTokenSource.Token, 100000000), cancellationTokenSource.Token); 9 10 Thread.Sleep(10); 11 //Thread.Sleep(1); 12 13 //传达取消请求 14 //这是异步请求,Task可能未完成也可能已经完成 15 cancellationTokenSource.Cancel(); 16 17 try 18 { 19 //若任务已取消,则Result会抛出AggregateException 20 Console.WriteLine("Sum is " + task.Result); 21 } 22 catch (AggregateException aggregateException) 23 { 24 //捕获方式一 25 //如果是OperationCanceledException,则返回true,表示已处理该异常 26 //如果不是,则返回false,表示该异常未处理,会抛出一个新的AggregateException 27 //aggregateException.Handle(handle => handle is OperationCanceledException); 28 29 //捕获方式二 30 //首先设置永远返回true,表示所有异常已处理,不再新抛出 31 aggregateException.Handle(handle => 32 { 33 //将任何OperationCanceledException对象都视为已处理(打印输出) 34 if (handle is OperationCanceledException) 35 { 36 Console.WriteLine("Sum was cancelled."); 37 } 38 else//如果不是OperationCanceledException,则也已处理(打印输出) 39 { 40 Console.WriteLine(aggregateException.Message); 41 foreach (var e in aggregateException.InnerExceptions) 42 { 43 Console.WriteLine(e.Message); 44 } 45 } 46 47 //注:若返回false,则其它(OperationCanceledException除外,因为上面已经处理过) 48 //任何异常都造成抛出一个新的AggregateException 49 //若返回true,则已处理异常,不再抛出(即使包含未处理的异常) 50 return true; 51 }); 52 53 //所有异常都处理完成后,执行下面的代码 54 Console.WriteLine("All exceptions are handled."); 55 } 56 } 57 58 /// <summary> 59 /// n以内正整数求和 60 /// </summary> 61 /// <param name="cancellationToken">取消操作的通知</param> 62 /// <param name="n"></param> 63 /// <returns></returns> 64 private static int Sum(CancellationToken cancellationToken, int n) 65 { 66 int sum = 0; 67 for (; n > 0; n--) 68 { 69 //在取消标识引用的CancellationTokenSource上调用Cancel 70 //如果已请求取消此标记,则引发 System.OperationCanceledException 71 cancellationToken.ThrowIfCancellationRequested(); 72 73 //检查n值,若太大,则抛出OverflowException 74 checked { sum = sum + n; } 75 } 76 return sum; 77 }
在创建Task时将一个CancellationToken传给构造器(如上例所示),从而将两者关联。但是,虽然Task对象关联了一个CancellationToken,但却没有办法访问它。因此,必须在Task的代码中获得创建Task对象时的同一个CancellationToken。为此,最简单的办法就是使用一个lambda表达式,将CancellationToken作为闭包变量“传递”(如上例所示)。如果CancellationToken在Task调度前取消(会抛出InvalidOperationException),Task会被取消,永远都不执行。但如果Task已调度(通过Start,或者Run,静态Run方法会自动创建Task对象并立即调用Start),那么Task的代码只有显式支持取消,其操作才能在执行期间取消。