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的代碼只有顯式支持取消,其操作才能在執行期間取消。