创建CancellationTokenSource对象用于取消Task
虽然使用线程池ThreadPool让我们使用多线程变得容易,但是因为是由系统来分配的,如果想对线程做精细的控制就不太容易了,比如某个线程结束后执行一个回调方法。恰好Task可以实现这样的需求。这篇文章我从以下几点对Task进行总结。
- 认识Task
- Task的用法
Task类在命名空间System.Threading.Tasks下,通过Task的Factory返回TaskFactory类,以TaskFactory.StartNew(Action)方法可以创建一个新的异步线程,所创建的线程默认为后台线程,不会影响前台UI窗口的运行。
如果要取消线程,可以利用CancellationTakenSource对象。如果要在取消任务后执行一个回调方法,则可以使用Task的()方法。
利用Task对之前的例子进行重写和扩展。代码如下。
namespace ThreadDemo { class Program { static void Main(string[] args) { // 创建CancellationTokenSource对象用于取消Task CancellationTokenSource cancelTokenSource = new CancellationTokenSource(); Fish fish1 = new Fish() { Name = "小黄鱼", Score = 1 }; Fish fish2 = new Fish() { Name = "大鲨鱼", Score = 100 }; // 创建一个Task Task task1 = new Task(() => fish1.Move(cancelTokenSource.Token), cancelTokenSource.Token); task1.Start(); // Task1被取消后的回调方法(小黄鱼被击中后显示积分) task1.ContinueWith(fish1.ShowScore); Task task2 = new Task(() => fish2.Move(cancelTokenSource.Token), cancelTokenSource.Token); task2.Start(); task2.ContinueWith(fish2.ShowScore); // 按任意键发射 Console.ReadKey(); // 武器工厂线程池 Gun gun = new Gun(); LaserGun laserGun = new LaserGun(); TaskFactory taskFactory = new TaskFactory(); Task[] tasks = new Task[] { taskFactory.StartNew(()=>gun.Fire()), taskFactory.StartNew(()=>laserGun.Fire()) }; // 执行武器开火 taskFactory.ContinueWhenAll(tasks, (Task) => { }); cancelTokenSource.Cancel(); Console.ReadKey(); } } /// <summary> /// 鱼 /// </summary> public class Fish { public string Name { get; set; } public int Score { get; set; } public Fish() { } public void Move() { Console.WriteLine(string.Format("{0}在游来游去...", Name)); } /// <summary> /// 游动 /// </summary> /// <param name="cancelToken"></param> public void Move(CancellationToken cancelToken) { while (!cancelToken.IsCancellationRequested) { Console.WriteLine(string.Format("{0}在游来游去...", Name)); // 阻塞1秒 Thread.Sleep(1000); } } /// <summary> /// 中枪后显示奖励 /// </summary> /// <param name="task"></param> public void ShowScore(Task task) { Console.WriteLine(string.Format("{0}中弹了,你得到{1}分",Name,Score)); } } }
程序运行结果如下:
eg2:
C# 使用 CancellationTokenSource 终止线程
使用CancellationTokenSource对象需要与Task对象进行配合使用,Task会对当前运行的状态进行控制(这个不用我们关心是如何孔控制的)。而CancellationTokenSource则是外部对Task的控制,如取消、定时取消。
下面我们来看看示例代码
- class Program
- {
- //声明CancellationTokenSource对象
- static CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
- //程序入口
- static void Main(string[] args)
- {
- Task.Factory.StartNew(MyTask, cancelTokenSource.Token);
- Console.WriteLine("请按回车键(Enter)停止");
- Console.ReadLine();
- cancelTokenSource.Cancel();
- Console.WriteLine("已停止");
- Console.ReadLine();
- }
- //测试方法
- static void MyTask()
- {
- //判断是否取消任务
- while (!cancelTokenSource.IsCancellationRequested)
- {
- Console.WriteLine(DateTime.Now);
- Thread.Sleep(1000);
- }
- }
- }
运行效果如图
Task.Factory.StartNew 创建并启动了 MyTask 方法,并传递了一个 CancellationTokenSource.Token 对象进去。我们可以通过在外部CancellationTokenSource对象进行控制是否取消任务的运行。
当在 MyTask 中的 cancelTokenSource.IsCancellationRequested 判断如果是取消了任务的话 就跳出while循环执行。也就结束了任务
我们还可以使用计时取消任务,当一个任务超过了我们所设定的时间然后自动取消该任务的执行。如下代码所示
- var cancelTokenSource = new CancellationTokenSource(3000);
除了构造函数,我们还可以使用另外一种方式实现定时取消,如下代码所示
- cancelTokenSource.CancelAfter(3000);
运行起来效果是一样的,3秒钟定时取消。
多个 CancellationTokenSource 复合
在有多个CancellationTokenSource需要一起并行管理的时候,比如任意一个任务取消 则取消所有任务。我们不必去一个一个的去关闭,只需要将需要一起并行关闭的CancellationTokenSource组合起来就行了。如下代码所示,执行结果是跟上面的图一样的。我就不再上图了。
- class Program
- {
- //声明CancellationTokenSource对象
- static CancellationTokenSource c1 = new CancellationTokenSource();
- static CancellationTokenSource c2 = new CancellationTokenSource();
- static CancellationTokenSource c3 = new CancellationTokenSource();
- //使用多个CancellationTokenSource进行复合管理
- static CancellationTokenSource compositeCancel = CancellationTokenSource.CreateLinkedTokenSource(c1.Token, c2.Token, c3.Token);
- //程序入口
- static void Main(string[] args)
- {
- Task.Factory.StartNew(MyTask, compositeCancel.Token);
- Console.WriteLine("请按回车键(Enter)停止");
- Console.ReadLine();
- //任意一个 CancellationTokenSource 取消任务,那么所有任务都会被取消。
- c1.Cancel();
- Console.WriteLine("已停止");
- Console.ReadLine();
- }
- //测试方法
- static void MyTask()
- {
- //判断是否取消任务
- while (!compositeCancel.IsCancellationRequested)
- {
- Console.WriteLine(DateTime.Now);
- Thread.Sleep(1000);
- }
- }
- }
以上代码调用了c1.Cancel();
触发了MyTask()
方法中的compositeCancel.IsCancellationRequested
为true,则取消了任务。所以我们在所有任务中判断复合的这个CancellationTokenSource对象即可。
相关文章
- java对象创建过程(jvm)
- 编写高质量代码改善C#程序的157个建议[动态数组、循环遍历、对象集合初始化]
- Context上下文对象(抄书的)
- 【COCOS2DX-LUA 脚本开发之五】LUA 使用OOP(面对对象思想编程),免BINDING创建自定义LUA类
- ExtJs之Ext.XTemplate:数组填充,访问父对象
- IE6 IE7 IE8(Q) 不支持 JSON 对象
- python垃圾回收示例及__del__(self)方法的使用(它会在对象被垃圾回收前调用)
- Java对象的创建过程
- Scala apply()方法用于创建伴生类对象
- ArcGIS Engine开发之旅08--和查询相关的对象和接口
- 给JavaScript的单个对象定义属性和属性的元数据
- Android应用程序窗口(Activity)窗口对象(Window)创建指南
- SSM三大框架之Spring创建和获取对象
- Jackson将Java对象转为json【或者】将json转为Java对象
- PHP面试题:单例模式,创建mysqli数据库链接的单例对象
- 已解决pandas正确创建DataFrame对象的四种方法(通过list列表、dict字典、ndarray、Series对象创建)
- 垃圾收集与对象生命拯救(读书笔记)
- Java //SR2.37 假设在程序中已经创建了一个Scanner类的对象myScanner和一个整型变量value,请编写程序提示用户输入他们的年龄,并将输入值存放到value变量中。
- littlevgl的控件父对象可以是img类型
- 【Groovy】MOP 元对象协议与元编程 ( 方法委托 | 正常方法调用 | 方法委托实现 | 代码示例 )
- 【Groovy】Groovy 脚本调用 ( Groovy 类中调用 Groovy 脚本 | 参考 Script#evaluate 方法 | 创建 Binding 对象并设置 args 参数 )
- 【MATLAB】进阶绘图 ( MATLAB 颜色值 | 条形图示例 | 查找对象属性方法 | 修改条形图属性 )
- 通过过滤器和增强request对象解决get提交请求服务器端乱码。
- 对象的创建和存在时间(持续更新)
- JAVAWEB开发之Session的追踪创建和销毁、JSP具体解释(指令,标签,内置对象,动作即转发和包括)、JavaBean及内省技术以及EL表达式获取内容的使用
- 直接读取表达矩阵和metadata信息创建seurat单细胞对象
- createGiottoVisiumObject从10x原始数据创建 giotto对象
- OC对象创建过程
- python里使用await关键字来等待Future对象完成
- python中创建dict对象
- 传输对象模式
- Spring MVC 分离了控制器、模型对象、过滤器以及处理程序对象的角色
- 【BOM】深入浅出浏览器对象模型—BOM基础知识详解_06
- 【项目实战】Protobuf入门介绍以及如何生成proto对象文件
- ADO.NET入门教程(七) 谈谈Command对象高级应用