C# CancellationTokenSource/CancellationToken

一. 传统的线程取消

   所谓的线程取消,就是线程正在执行的过程中取消线程任务。

   传统的线程取消,是通过一个变量来控制,但是这种方式,在release模式下,被优化从cpu高速缓存中读取,而不是从内存中读取,会造成主线程无法执行这一个bug。

复制代码

  1. 1 {
  2. 2 var isStop = false;
  3. 3 var thread = new Thread(() =>
  4. 4 {
  5. 5 while (!isStop)
  6. 6 {
  7. 7 Thread.Sleep(100);
  8. 8 Console.WriteLine("当前thread={0} 正在运行", Thread.CurrentThread.ManagedThreadId);
  9. 9 }
  10. 10 });
  11. 11 thread.Start();
  12. 12 Thread.Sleep(1000);
  13. 13 isStop = true;
  14. 14 }

复制代码

 

PS: 通过上面的代码看可以看出来,传统模式的线程取消,在排除release模式bug的情况下,局限性还是很明显的。比如:当子线程任务取消的那一刻,我想执行另外一项任务;我想延时取消一个线程任务;线程取消的时候抛异常。

  上述这几种情况,我们都要借助单独的类来处理。

 

二. CancellationTokenSource实现任务取消 

1. 取消任务的同时触发一个函数

   利用Cancel方法、Register注册、source.Token标记取消位来实现。

复制代码

  1. {
  2. CancellationTokenSource source = new CancellationTokenSource();
  3. //注册一个线程取消后执行的逻辑
  4. source.Token.Register(() =>
  5. {
  6. //这里执行线程被取消后的业务逻辑.
  7. Console.WriteLine("-------------我是线程被取消后的业务逻辑---------------------");
  8. });
  9. Task.Run(() =>
  10. {
  11. while (!source.IsCancellationRequested)
  12. {
  13. Thread.Sleep(100);
  14. Console.WriteLine("当前thread={0} 正在运行", Thread.CurrentThread.ManagedThreadId);
  15. }
  16. }, source.Token);
  17. Thread.Sleep(2000);
  18. source.Cancel();
  19. }

复制代码

2. 延时取消

线程的延时取消有两种方式:

  方案一:CancelAfter方法。

复制代码

  1. 1 #region 方案一:CancelAfter方法
  2. 2 {
  3. 3 CancellationTokenSource source = new CancellationTokenSource();
  4. 4 //注册一个线程取消后执行的逻辑
  5. 5 source.Token.Register(() =>
  6. 6 {
  7. 7 //这里执行线程被取消后的业务逻辑.
  8. 8 Console.WriteLine("-------------我是线程被取消后的业务逻辑---------------------");
  9. 9 });
  10. 10
  11. 11 Task.Run(() =>
  12. 12 {
  13. 13 while (!source.IsCancellationRequested)
  14. 14 {
  15. 15 Thread.Sleep(100);
  16. 16 Console.WriteLine("当前thread={0} 正在运行", Thread.CurrentThread.ManagedThreadId);
  17. 17 }
  18. 18 }, source.Token);
  19. 19
  20. 20 Thread.Sleep(2000);
  21. 21 //4s后自动取消
  22. 22 source.CancelAfter(new TimeSpan(0, 0, 0, 4));
  23. 23 }
  24. 24 #endregion

复制代码

  方案二:CancellationTokenSource构造函数(不再需要Cancel方法了)。

复制代码

  1. 1 {
  2. 2 //4s后自动取消
  3. 3 CancellationTokenSource source = new CancellationTokenSource(4000);
  4. 4 //注册一个线程取消后执行的逻辑
  5. 5 source.Token.Register(() =>
  6. 6 {
  7. 7 //这里执行线程被取消后的业务逻辑.
  8. 8 Console.WriteLine("-------------我是线程被取消后的业务逻辑---------------------");
  9. 9 });
  10. 10
  11. 11 Task.Run(() =>
  12. 12 {
  13. 13 while (!source.IsCancellationRequested)
  14. 14 {
  15. 15 Thread.Sleep(100);
  16. 16 Console.WriteLine("当前thread={0} 正在运行", Thread.CurrentThread.ManagedThreadId);
  17. 17 }
  18. 18 }, source.Token);
  19. 19
  20. 20 Thread.Sleep(2000);
  21. 21 }

复制代码

 

3. 组合取消

   利用CreateLinkedTokenSource构建CancellationTokenSource的组合体,其中任何一个体取消,则组合体就取消。 

复制代码

  1. {
  2. CancellationTokenSource source1 = new CancellationTokenSource();
  3. //source1.Cancel();
  4. CancellationTokenSource source2 = new CancellationTokenSource();
  5. source2.Cancel();
  6. var combineSource = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token);
  7. Console.WriteLine("s1={0} s2={1} s3={2}", source1.IsCancellationRequested,
  8. source2.IsCancellationRequested,
  9. combineSource.IsCancellationRequested);
  10. }

复制代码

  上述代码,source1和source2中的任何一个取消,combineSource就会被取消。

 

三. CancellationToken类监控取消

   CancellationToken类下ThrowIfCancellationRequested属性,等价于if (XXX.IsCancellationRequested){throw new Exception("报错了");}

   只要取消就报错。

复制代码

  1. 1 {
  2. 2 CancellationTokenSource source1 = new CancellationTokenSource();
  3. 3 CancellationTokenSource source2 = new CancellationTokenSource();
  4. 4 var combineSource = CancellationTokenSource.CreateLinkedTokenSource(source1.Token, source2.Token);
  5. 5 source1.Cancel();
  6. 6
  7. 7 //if (combineSource.IsCancellationRequested)
  8. 8 //{
  9. 9 // throw new Exception("报错了");
  10. 10 //}
  11. 11
  12. 12 //等价于上面那句话
  13. 13 try
  14. 14 {
  15. 15 combineSource.Token.ThrowIfCancellationRequested();
  16. 16 }
  17. 17 catch (Exception)
  18. 18 {
  19. 19 Console.WriteLine("报错了");
  20. 20 }
  21. 21
  22. 22
  23. 23 Console.WriteLine("s1={0} s2={1} s3={2}", source1.IsCancellationRequested,
  24. 24 source2.IsCancellationRequested,
  25. 25 combineSource.IsCancellationRequested);
  26. 26 }
posted @ 2022-06-13 23:57  懒树懒  阅读(1500)  评论(0)    收藏  举报