一. 傳統的線程取消
所謂的線程取消,就是線程正在執行的過程中取消線程任務。
傳統的線程取消,是通過一個變量來控制,但是這種方式,在release模式下,被優化從cpu高速緩存中讀取,而不是從內存中讀取,會造成主線程無法執行這一個bug。
1 { 2 var isStop = false; 3 var thread = new Thread(() => 4 { 5 while (!isStop) 6 { 7 Thread.Sleep(100); 8 Console.WriteLine("當前thread={0} 正在運行", Thread.CurrentThread.ManagedThreadId); 9 } 10 }); 11 thread.Start(); 12 Thread.Sleep(1000); 13 isStop = true; 14 }
PS: 通過上面的代碼看可以看出來,傳統模式的線程取消,在排除release模式bug的情況下,局限性還是很明顯的。比如:當子線程任務取消的那一刻,我想執行另外一項任務;我想延時取消一個線程任務;線程取消的時候拋異常。
上述這幾種情況,我們都要借助單獨的類來處理。
二. CancellationTokenSource實現任務取消
1. 取消任務的同時觸發一個函數
利用Cancel方法、Register注冊、source.Token標記取消位來實現。
{ CancellationTokenSource source = new CancellationTokenSource(); //注冊一個線程取消后執行的邏輯 source.Token.Register(() => { //這里執行線程被取消后的業務邏輯. Console.WriteLine("-------------我是線程被取消后的業務邏輯---------------------"); }); Task.Run(() => { while (!source.IsCancellationRequested) { Thread.Sleep(100); Console.WriteLine("當前thread={0} 正在運行", Thread.CurrentThread.ManagedThreadId); } }, source.Token); Thread.Sleep(2000); source.Cancel(); }
2. 延時取消
線程的延時取消有兩種方式:
方案一:CancelAfter方法。

1 #region 方案一:CancelAfter方法 2 { 3 CancellationTokenSource source = new CancellationTokenSource(); 4 //注冊一個線程取消后執行的邏輯 5 source.Token.Register(() => 6 { 7 //這里執行線程被取消后的業務邏輯. 8 Console.WriteLine("-------------我是線程被取消后的業務邏輯---------------------"); 9 }); 10 11 Task.Run(() => 12 { 13 while (!source.IsCancellationRequested) 14 { 15 Thread.Sleep(100); 16 Console.WriteLine("當前thread={0} 正在運行", Thread.CurrentThread.ManagedThreadId); 17 } 18 }, source.Token); 19 20 Thread.Sleep(2000); 21 //4s后自動取消 22 source.CancelAfter(new TimeSpan(0, 0, 0, 4)); 23 } 24 #endregion
方案二:CancellationTokenSource構造函數(不再需要Cancel方法了)。

1 { 2 //4s后自動取消 3 CancellationTokenSource source = new CancellationTokenSource(4000); 4 //注冊一個線程取消后執行的邏輯 5 source.Token.Register(() => 6 { 7 //這里執行線程被取消后的業務邏輯. 8 Console.WriteLine("-------------我是線程被取消后的業務邏輯---------------------"); 9 }); 10 11 Task.Run(() => 12 { 13 while (!source.IsCancellationRequested) 14 { 15 Thread.Sleep(100); 16 Console.WriteLine("當前thread={0} 正在運行", Thread.CurrentThread.ManagedThreadId); 17 } 18 }, source.Token); 19 20 Thread.Sleep(2000); 21 }
3. 組合取消
利用CreateLinkedTokenSource構建CancellationTokenSource的組合體,其中任何一個體取消,則組合體就取消。
{ CancellationTokenSource source1 = new CancellationTokenSource(); //source1.Cancel(); CancellationTokenSource source2 = new CancellationTokenSource(); source2.Cancel(); var combineSource = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token); Console.WriteLine("s1={0} s2={1} s3={2}", source1.IsCancellationRequested, source2.IsCancellationRequested, combineSource.IsCancellationRequested); }
上述代碼,source1和source2中的任何一個取消,combineSource就會被取消。
三. CancellationToken類監控取消
CancellationToken類下ThrowIfCancellationRequested屬性,等價於if (XXX.IsCancellationRequested){throw new Exception("報錯了");}
只要取消就報錯。
1 { 2 CancellationTokenSource source1 = new CancellationTokenSource(); 3 CancellationTokenSource source2 = new CancellationTokenSource(); 4 var combineSource = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token); 5 source1.Cancel(); 6 7 //if (combineSource.IsCancellationRequested) 8 //{ 9 // throw new Exception("報錯了"); 10 //} 11 12 //等價於上面那句話 13 try 14 { 15 combineSource.Token.ThrowIfCancellationRequested(); 16 } 17 catch (Exception) 18 { 19 Console.WriteLine("報錯了"); 20 } 21 22 23 Console.WriteLine("s1={0} s2={1} s3={2}", source1.IsCancellationRequested, 24 source2.IsCancellationRequested, 25 combineSource.IsCancellationRequested); 26 }