原文:http://www.cnblogs.com/Leo_wl/archive/2010/06/01/1749596.html
前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程常常打交道的类,所以,对Task对全面的了解很有必要。
上篇文章主要讲述了如何创建一个task,本篇文章主要讲述如何取消一个task。
本篇主的主要议题如下:
1. 通过轮询的方式检测Task是否被取消
2. 用委托delegate来检测Task是否被取消
3. 用Wait Handle还检测Task是否被取消
4. 取消多个Task
5. 创建组合的取消Task的Token
6. 判断一个Task是否已经被取消了

本篇的理论不多,代码的例子很多。

在TPL中一个标准化的操作就是”取消Task”。之所以说它是个标准化的操作,其实是把这个操作和之前传统的多线程编程进行比较而言的。

在之前的多线程编程中,我们一般是自己写一些代码来取消线程的运行。但是在.NET 4的TPL中就内置了取消的方法,可能我们觉得TPL没有必要内置这些代码,因为太简单了。但是这个内置的方法不仅仅只是取消了运行的Task,而且还减小了在取消运行的Task时可能产生的一些风险,我们后续文章会详细讲述。

创建一个取消的Task一般要进行下面一些步骤:

a.创建System.Threading.CancellationTokenSource的一个实例:

  1. // create the cancellation token source
  2. CancellationTokenSource tokenSource = new CancellationTokenSource();

b. 通过CancellationTokenSource.Token属性获得一个System.Threading.CancellationToken:

  1. CancellationToken token = tokenSource.Token;

c.创建一个新的Task或者Task<T>,并且在构造函数传入Action或者Action<object>的委托作为第一个参数,传入CancellationToken作为第二个参数:

  1. Task task = new Task(new Action(printMessage), token);

d.调用Task的Start()方法。
上面的步骤和我们之前介绍的创建一个Task的代码几乎一样,只是在构造函数中多传入了一个参数。
如果想要取消一个Task的运行,只要调用CancellationToken实例的Cancel()方法就可以了。
有点要特别注意的,当我们调用了Cancel()方法之后,.NET Framework不会强制性的去关闭运行的Task。
我们自己必须去检测之前在创建Task时候传入的那个CancellationToken。
我们在创建Task是传入CancellationToken到构造函数,其实这个CancellationToken就是.NET Framework用来避免我们再次运行已经被取消的Task,可以说就是一个标志位。

  首先,进入第一个议题:
  1. 通过轮询的方式检测Task是否被取消
  在很多Task内部都包含了循环,用来处理数据。我们可以在循环中通过CancellationToken的IsCancellationRequest属性来检测task是否被取消了。如果这个属性为true,那么我们就得跳出循环,并且释放task所占用的资源(如数据库资源,文件资源等).
  我们也可以在task运行体中抛出System.Threading.OperationCanceledException来取消运行的task。
    代码如下:

  1. while (true)
  2. {
  3. if (token.IsCancellationRequested)
  4. {
  5. // tidy up and release resources
  6. throw new OperationCanceledException(token);
  7. }
  8. else
  9. {
  10. // do a unit of work
  11. }
  12. }

  如果我们没有任何的资源要释放,那么只要简单的调用CancellationToken.ThrowIfCancellationRequested()方法,这个方法会检查是否要取消task,并且抛出异常。代码如下:

  1. while (true)
  2. {
  3. token.ThrowIfCancellationRequested();
  4. // do a unit of work
  5. }

  下面就给出有一个完整的例子:创建一个可以取消的task,并且通过轮询不断的检查是否要取消task

  代码如下:

  1. static void Main(string[] args)
  2. {
  3. // create the cancellation token source
  4. CancellationTokenSource tokenSource = new CancellationTokenSource();
  5.  
  6. // create the cancellation token
  7. CancellationToken token = tokenSource.Token;
  8. // create the task
  9.  
  10. Task task = new Task(() =>
  11. {
  12. for (int i = ; i < int.MaxValue; i++)
  13. {
  14. if (token.IsCancellationRequested)
  15. {
  16. Console.WriteLine("Task cancel detected");
  17. throw new OperationCanceledException(token);
  18. }
  19. else
  20. {
  21. Console.WriteLine("Int value {0}", i);
  22. }
  23. }
  24. }, token);
  25.  
  26. // wait for input before we start the task
  27. Console.WriteLine("Press enter to start task");
  28. Console.WriteLine("Press enter again to cancel task");
  29. Console.ReadLine();
  30.  
  31. // start the task
  32. task.Start();
  33.  
  34. // read a line from the console.
  35. Console.ReadLine();
  36.  
  37. // cancel the task
  38. Console.WriteLine("Cancelling task");
  39. tokenSource.Cancel();
  40.  
  41. // wait for input before exiting
  42. Console.WriteLine("Main method complete. Press enter to finish.");
  43. Console.ReadLine();
  44. }

2. 用委托delegate来检测Task是否被取消

  我们可以在注册一个委托到CancellationToken中,这个委托的方法在CancellationToken.Cancel()调用之前被调用。
  我们可以用这个委托中的方法来作为一个检测task是否被取消的另外一个可选的方法,因为这个方法是在Cancel()方法被调用之前就调用的,所以这个委托中的方法可以检测task是否被cancel了,也就是说,只要这个委托的方法被调用,那么就说这个CancellationToken.Cancel()方法被调用了,而且在这个委托的方法中我们可以做很多的事情,如通知用户取消操作发生了。
  下面的代码给出了一个例子。

  1. static void Main(string[] args)
  2. {
  3. // create the cancellation token source
  4. CancellationTokenSource tokenSource = new CancellationTokenSource();
  5.  
  6. // create the cancellation token
  7. CancellationToken token = tokenSource.Token;
  8.  
  9. // create the task
  10. Task task = new Task(() =>
  11. {
  12. for (int i = ; i < int.MaxValue; i++)
  13. {
  14. if (token.IsCancellationRequested)
  15. {
  16. Console.WriteLine("Task cancel detected");
  17. throw new OperationCanceledException(token);
  18. }
  19. else
  20. {
  21. Console.WriteLine("Int value {0}", i);
  22. }
  23. }
  24. }, token);
  25.  
  26. // register a cancellation delegate
  27. token.Register(() =>
  28. {
  29. Console.WriteLine(">>>>>> Delegate Invoked\n");
  30. });
  31.  
  32. // wait for input before we start the task
  33. Console.WriteLine("Press enter to start task");
  34. Console.WriteLine("Press enter again to cancel task");
  35. Console.ReadLine();
  36.  
  37. // start the task
  38. task.Start();
  39. // read a line from the console.
  40. Console.ReadLine();
  41.  
  42. // cancel the task
  43. Console.WriteLine("Cancelling task");
  44. tokenSource.Cancel();
  45.  
  46. // wait for input before exiting
  47. Console.WriteLine("Main method complete. Press enter to finish.");
  48. Console.ReadLine();
  49. }

3. 用Wait Handle还检测Task是否被取消

  第三种方法检测task是否被cancel就是调用CancellationToken.WaitHandle属性。对于这个属性的详细使用,在后续的文章中会深入的讲述,在这里主要知道一点就行了:CancellationToken的WaitOne()方法会阻止task的运行,只有CancellationToken的cancel()方法被调用后,这种阻止才会释放。
  在下面的例子中,创建了两个task,其中task2调用了WaitOne()方法,所以task2一直不会运行,除非调用了CancellationToken的Cancel()方法,所以WaitOne()方法也算是检测task是否被cancel的一种方法了。

  1. static void Main(string[] args)
  2. {
  3.  
  4. // create the cancellation token source
  5. CancellationTokenSource tokenSource = new CancellationTokenSource();
  6.  
  7. // create the cancellation token
  8. CancellationToken token = tokenSource.Token;
  9.  
  10. // create the task
  11. Task task1 = new Task(() =>
  12. {
  13. for (int i = ; i < int.MaxValue; i++)
  14. {
  15. if (token.IsCancellationRequested)
  16. {
  17. Console.WriteLine("Task cancel detected");
  18. throw new OperationCanceledException(token);
  19. }
  20. else
  21. {
  22. Console.WriteLine("Int value {0}", i);
  23. }
  24. }
  25. }, token);
  26.  
  27. // create a second task that will use the wait handle
  28. Task task2 = new Task(() =>
  29. {
  30. // wait on the handle
  31. token.WaitHandle.WaitOne();
  32. // write out a message
  33. Console.WriteLine(">>>>> Wait handle released");
  34. });
  35.  
  36. // wait for input before we start the task
  37. Console.WriteLine("Press enter to start task");
  38. Console.WriteLine("Press enter again to cancel task");
  39. Console.ReadLine();
  40. // start the tasks
  41. task1.Start();
  42. task2.Start();
  43.  
  44. // read a line from the console.
  45. Console.ReadLine();
  46.  
  47. // cancel the task
  48. Console.WriteLine("Cancelling task");
  49. tokenSource.Cancel();
  50.  
  51. // wait for input before exiting
  52. Console.WriteLine("Main method complete. Press enter to finish.");
  53. Console.ReadLine();
  54. }

4. 取消多个Task

  我们可以使用一个CancellationToken来创建多个不同的Tasks,当这个CancellationToken的Cancel()方法调用的时候,使用了这个token的多个task都会被取消。

  1. static void Main(string[] args)
  2. {
  3. // create the cancellation token source
  4. CancellationTokenSource tokenSource = new CancellationTokenSource();
  5.  
  6. // create the cancellation token
  7. CancellationToken token = tokenSource.Token;
  8.  
  9. // create the tasks
  10. Task task1 = new Task(() =>
  11. {
  12. for (int i = ; i < int.MaxValue; i++)
  13. {
  14. token.ThrowIfCancellationRequested();
  15. Console.WriteLine("Task 1 - Int value {0}", i);
  16. }
  17. }, token);
  18.  
  19. Task task2 = new Task(() =>
  20. {
  21. for (int i = ; i < int.MaxValue; i++)
  22. {
  23. token.ThrowIfCancellationRequested();
  24. Console.WriteLine("Task 2 - Int value {0}", i);
  25. }
  26. }, token);
  27. // wait for input before we start the tasks
  28. Console.WriteLine("Press enter to start tasks");
  29. Console.WriteLine("Press enter again to cancel tasks");
  30. Console.ReadLine();
  31.  
  32. // start the tasks
  33. task1.Start();
  34. task2.Start();
  35.  
  36. // read a line from the console.
  37. Console.ReadLine();
  38.  
  39. // cancel the task
  40. Console.WriteLine("Cancelling tasks");
  41. tokenSource.Cancel();
  42. // wait for input before exiting
  43. Console.WriteLine("Main method complete. Press enter to finish.");
  44. Console.ReadLine();
  45. }

5. 创建组合的取消Task的Token

  我们可以用CancellationTokenSource.CreateLinkedTokenSource()方法来创建一个组合的token,这个组合的token有很多的CancellationToken组成。主要组合token中的任意一个token调用了Cancel()方法,那么使用这个组合token的所有task就会被取消。代码如下:

  1. static void Main(string[] args)
  2. {
  3. // create the cancellation token sources
  4. CancellationTokenSource tokenSource1 = new CancellationTokenSource();
  5. CancellationTokenSource tokenSource2 = new CancellationTokenSource();
  6. CancellationTokenSource tokenSource3 = new CancellationTokenSource();
  7.  
  8. // create a composite token source using multiple tokens
  9. CancellationTokenSource compositeSource =
  10. CancellationTokenSource.CreateLinkedTokenSource(
  11. tokenSource1.Token, tokenSource2.Token, tokenSource3.Token);
  12.  
  13. // create a cancellable task using the composite token
  14. Task task = new Task(() =>
  15. {
  16. // wait until the token has been cancelled
  17. compositeSource.Token.WaitHandle.WaitOne();
  18. // throw a cancellation exception
  19. throw new OperationCanceledException(compositeSource.Token);
  20. }, compositeSource.Token);
  21.  
  22. // start the task
  23. task.Start();
  24.  
  25. // cancel one of the original tokens
  26. tokenSource2.Cancel();
  27.  
  28. // wait for input before exiting
  29. Console.WriteLine("Main method complete. Press enter to finish.");
  30. Console.ReadLine();
  31. }

6. 判断一个Task是否已经被取消了

  可以使用Task的IsCancelled属性来判断task是否被取消了。代码如下:

  1. static void Main(string[] args)
  2. {
  3. // create the cancellation token source
  4. CancellationTokenSource tokenSource1 = new CancellationTokenSource();
  5.  
  6. // create the cancellation token
  7. CancellationToken token1 = tokenSource1.Token;
  8.  
  9. // create the first task, which we will let run fully
  10. Task task1 = new Task(() =>
  11. {
  12. for (int i = ; i < ; i++)
  13. {
  14. token1.ThrowIfCancellationRequested();
  15. Console.WriteLine("Task 1 - Int value {0}", i);
  16. }
  17. }, token1);
  18.  
  19. // create the second cancellation token source
  20. CancellationTokenSource tokenSource2 = new CancellationTokenSource();
  21.  
  22. // create the cancellation token
  23. CancellationToken token2 = tokenSource2.Token;
  24.  
  25. // create the second task, which we will cancel
  26. Task task2 = new Task(() =>
  27. {
  28. for (int i = ; i < int.MaxValue; i++)
  29. {
  30. token2.ThrowIfCancellationRequested();
  31. Console.WriteLine("Task 2 - Int value {0}", i);
  32. }
  33. }, token2);
  34.  
  35. // start all of the tasks
  36. task1.Start();
  37. task2.Start();
  38.  
  39. // cancel the second token source
  40. tokenSource2.Cancel();
  41. // write out the cancellation detail of each task
  42. Console.WriteLine("Task 1 cancelled? {0}", task1.IsCanceled);
  43. Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);
  44. // wait for input before exiting
  45. Console.WriteLine("Main method complete. Press enter to finish.");
  46. Console.ReadLine();
  47. }

天就写到这里,比较的简单,都是一些很基础的东西,只有这样,后面深入讲解的时候才更加的顺利。

谢谢各位!

.NET 4并行编程入门之Task的取消[转]的更多相关文章

  1. C#并行编程-Task

    菜鸟学习并行编程,参考<C#并行编程高级教程.PDF>,如有错误,欢迎指正. 目录 C#并行编程-相关概念 C#并行编程-Parallel C#并行编程-Task C#并行编程-并发集合 ...

  2. 《OpenCL异构并行编程实战》补充笔记散点,第一至四章

    ▶ 总体印象:适合 OpenCL 入门的书,有丰富的代码和说明,例子较为简单.先把 OpenCL 代码的基本结构(平台 → 设备 → 上下文 → 命令队列 → 创建缓冲区 → 读写缓冲区 → 编译代码 ...

  3. 一、并行编程 - 数据并行 System.Threading.Tasks.Parallel 类

    一.并行概念 1.并行编程 在.NET 4中的并行编程是依赖Task Parallel Library(后面简称为TPL) 实现的.在TPL中,最基本的执行单元是task(中文可以理解为"任 ...

  4. .NET 并行(多核)编程系列之五 Task执行和异常处理

    原文:.NET 并行(多核)编程系列之五 Task执行和异常处理 .NET 并行(多核)编程系列之五 Task执行和异常处理 前言:本篇主要讲述等待task执行完成. 本篇的议题如下: 1. 等待Ta ...

  5. .NET 并行(多核)编程系列之六 Task基础部分完结篇

    原文:.NET 并行(多核)编程系列之六 Task基础部分完结篇 .NET 并行(多核)编程系列之六 Task基础部分完结篇 前言:之前的文章介绍了了并行编程的一些基本的,也注重的讲述了Task的一些 ...

  6. .NET 4 并行(多核)编程系列之三 从Task的取消

    原文:.NET 4 并行(多核)编程系列之三 从Task的取消 .NET 4 并行(多核)编程系列之三 从Task的取消 前言:因为Task是.NET 4并行编程最为核心的一个类,也我们在是在并行编程 ...

  7. C#~异步编程再续~大叔所理解的并行编程(Task&Parallel)

    返回目录 并行这个概念出自.net4.5,它被封装在System.Threading.Tasks命名空间里,主要提供一些线程,异步的方法,或者说它是对之前Thread进行的二次封装,为的是让开发人员更 ...

  8. .NET 4 并行(多核)编程系列之四 Task的休眠

    原文:.NET 4 并行(多核)编程系列之四 Task的休眠 .NET 4 并行(多核)编程系列之四 Task的休眠 前言:之前的几篇文章断断续续的介绍了Task的一些功能:创建,取消.本篇介绍Tas ...

  9. 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等

    随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...

随机推荐

  1. repositoryItemButtonEdit ButtonClick没有反应的原因

    今天在gridcontrol中做了一个按键列,增加单击事件后,却发现不能触发. 原因:设置了GridControl了Editable为false! 设置为true后,点击触发. 如果数据"不 ...

  2. CentOS重置Mysql密码

    1.首先确认服务器出于安全的状态,也就是没有人能够任意地连接MySQL数据库. 因为在重新设置MySQL的root密码的期间,MySQL数据库完全出于没有密码保护的 状态下,其他的用户也可以任意地登录 ...

  3. velocity单引号与双引号

    (1)最外层是用单引号包围时,双引号直接使用就可以了,两个连续的单引号表示一个单引号:#set($var2 = 'A"B''C') --> $var2 的值为 A"B'C(2 ...

  4. <跟股市谚语学炒股> 读书笔记

    书在这里 一般情况下,当成交清单上显示的买盘金额大.笔数少,卖盘金额小.笔数多时,系主力在建仓.散户在卖出:相反,若买盘金额小.笔数多,卖盘金额大.笔数少时,系主力在出货.散户在买入 一般来说,当大盘 ...

  5. [shell]shell 中| && || () {} 用法以及shell的逻辑与或非

    转自:https://www.jianshu.com/p/617c1ee1e46e | 运算符 管道符号,是unix一个很强大的功能,符号为一条竖线:"|".用法: command ...

  6. Redis提供的持久化机制(二)

    1.前言 Redis是一种高级key-value数据库.它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富.有字符串,链表,集 合和有序集合.支持在服务器端计算集合的并,交和补集 ...

  7. hbase源码系列(五)Trie单词查找树

    在上一章中提到了编码压缩,讲了一个简单的DataBlockEncoding.PREFIX算法,它用的是前序编码压缩的算法,它搜索到时候,是全扫描的方式搜索的,如此一来,搜索效率实在是不敢恭维,所以在h ...

  8. mysql show global variables字符超1024会被截断

    show variables 会存在数据被截断的问题: select 全局变量没有问题 官网解释:https://dev.mysql.com/doc/refman/5.6/en/variables-t ...

  9. apk签名打包时报master password is required to unlock the password database.错误,或者signtrue versions无法勾选,以及Error:Execution failed for task ':app:lintVitalRelease'.

    1.如果在签名时android studio报"Master password is required to unlock the password database.The passwor ...

  10. thinkphp中memcache的用法实例

    本文实例讲述了thinkphp中memcache的用法.分享给大家供大家参考.具体分析如下: 1.下载并安装memcache ① window下安装memcache. 下载memcached.exe ...