第七節:利用CancellationTokenSource實現任務取消和利用CancellationToken類檢測取消異常。


一. 傳統的線程取消

   所謂的線程取消,就是線程正在執行的過程中取消線程任務。

   傳統的線程取消,是通過一個變量來控制,但是這種方式,在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
View Code

  方案二: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                 } 
View Code

 

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             }

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM