C# 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
方案二: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 }
重复就是力量,数量堆死质量

浙公网安备 33010602011771号