第七节:利用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             }

 

 

 

 

 

 

posted @ 2018-01-07 19:03  Yaopengfei  阅读(11866)  评论(4编辑  收藏  举报