5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
5天玩转C#并行和多线程编程系列文章目录
5天玩转C#并行和多线程编程 —— 第一天 认识Parallel
5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task
5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结
对于多线程,我们经常使用的是Thread。在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢?
任务和线程的区别:
1、任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
2、任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。
1、认识Task
首先来看一下Task的继承结构。Task标识一个异步操作。
可以看到Task和Thread一样,位于System.Threading命名空间下,这也就是说他们直接有密不可分的联系。下面我们来仔细看一下吧!
2、创建Task
创建Task的方法有两种,一种是直接创建——new一个出来,一种是通过工厂创建。下面来看一下这两种创建方法:
- //第一种创建方式,直接实例化
- var task1 = new Task(() =>
- {
- //TODO you code
- });
这是最简单的创建方法,可以看到其构造函数是一个Action,其构造函数有如下几种,比较常用的是前两种。
- //第二种创建方式,工厂创建
- var task2 = Task.Factory.StartNew(() =>
- {
- //TODO you code
- });
这种方式通过静态工厂,创建以个Task并运行。下面我们来建一个控制台项目,演示一下,代码如下:
要添加System.Threading.Tasks命名控件引用。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace TaskDemo
- {
- class Program
- {
- static void Main(string[] args)
- {
- var task1 = new Task(() =>
- {
- Console.WriteLine("Hello,task");
- });
- task1.Start();
- var task2 = Task.Factory.StartNew(() =>
- {
- Console.WriteLine("Hello,task started by task factory");
- });
- Console.Read();
- }
- }
- }
这里我分别用两种方式创建两个task,并让他们运行。可以看到通过构造函数创建的task,必须手动Start,而通过工厂创建的Task直接就启动了。
下面我们来看一下Task的声明周期,编写如下代码:
- var task1 = new Task(() =>
- {
- Console.WriteLine("Begin");
- System.Threading.Thread.Sleep();
- Console.WriteLine("Finish");
- });
- Console.WriteLine("Before start:" + task1.Status);
- task1.Start();
- Console.WriteLine("After start:" + task1.Status);
- task1.Wait();
- Console.WriteLine("After Finish:" + task1.Status);
- Console.Read();
task1.Status就是输出task的当前状态,其输出结果如下:
可以看到调用Start前的状态是Created,然后等待分配线程去执行,到最后执行完成。
从我们可以得出Task的简略生命周期:
Created:表示默认初始化任务,但是“工厂创建的”实例直接跳过。
WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。
RanToCompletion:任务执行完毕。
Task最吸引人的地方就是他的任务控制了,你可以很好的控制task的执行顺序,让多个task有序的工作。下面来详细说一下:
1、Task.Wait
在上个例子中,我们已经使用过了,task1.Wait();就是等待任务执行完成,我们可以看到最后task1的状态变为Completed。
2、Task.WaitAll
看字面意思就知道,就是等待所有的任务都执行完成,下面我们来写一段代码演示一下:
- static void Main(string[] args)
- {
- var task1 = new Task(() =>
- {
- Console.WriteLine("Task 1 Begin");
- System.Threading.Thread.Sleep();
- Console.WriteLine("Task 1 Finish");
- });
- var task2 = new Task(() =>
- {
- Console.WriteLine("Task 2 Begin");
- System.Threading.Thread.Sleep();
- Console.WriteLine("Task 2 Finish");
- });
- task1.Start();
- task2.Start();
- Task.WaitAll(task1, task2);
- Console.WriteLine("All task finished!");
- Console.Read();
- }
其输出结果如下:
可以看到,任务一和任务二都完成以后,才输出All task finished!
3、Task.WaitAny
这个用发同Task.WaitAll,就是等待任何一个任务完成就继续向下执行,将上面的代码WaitAll替换为WaitAny,输出结果如下:
4、Task.ContinueWith
就是在第一个Task完成后自动启动下一个Task,实现Task的延续,下面我们来看下他的用法,编写如下代码:
- static void Main(string[] args)
- {
- var task1 = new Task(() =>
- {
- Console.WriteLine("Task 1 Begin");
- System.Threading.Thread.Sleep();
- Console.WriteLine("Task 1 Finish");
- });
- var task2 = new Task(() =>
- {
- Console.WriteLine("Task 2 Begin");
- System.Threading.Thread.Sleep();
- Console.WriteLine("Task 2 Finish");
- });
- task1.Start();
- task2.Start();
- var result = task1.ContinueWith<string>(task =>
- {
- Console.WriteLine("task1 finished!");
- return "This is task result!";
- });
- Console.WriteLine(result.Result.ToString());
- Console.Read();
- }
执行结果如下:
可以看到,task1完成之后,开始执行后面的内容,并且这里我们取得task的返回值。
在每次调用ContinueWith方法时,每次会把上次Task的引用传入进来,以便检测上次Task的状态,比如我们可以使用上次Task的Result属性来获取返回值。我们还可以这么写:
- var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); })
- .ContinueWith<bool>(s => { return true; })
- .ContinueWith<string>(r =>
- {
- if (r.Result)
- {
- return "Finished";
- }
- else
- {
- return "Error";
- }
- });
- Console.WriteLine(SendFeedBackTask.Result);
首先输出Get some data,然后执行第二个获得返回值true,最后根据判断返回Finished或error。输出结果:
Get some Data!
Finished
其实上面的写法简化一下,可以这样写:
- Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});
输出One,这个可以看明白了吧~
更多ContinueWith用法参见:http://technet.microsoft.com/zh-CN/library/dd321405
5、Task的取消
前面说了那么多Task的用法,下面来说下Task的取消,比如我们启动了一个task,出现异常或者用户点击取消等等,我们可以取消这个任务。
如何取消一个Task呢,我们通过cancellation的tokens来取消一个Task。在很多Task的Body里面包含循环,我们可以在轮询的时候判断IsCancellationRequested属性是否为True,如果是True的话就return或者抛出异常,抛出异常后面再说,因为还没有说异常处理的东西。
下面在代码中看下如何实现任务的取消,代码如下:
- var tokenSource = new CancellationTokenSource();
- var token = tokenSource.Token;
- var task = Task.Factory.StartNew(() =>
- {
- for (var i = ; i < ; i++)
- {
- System.Threading.Thread.Sleep();
- if (token.IsCancellationRequested)
- {
- Console.WriteLine("Abort mission success!");
- return;
- }
- }
- }, token);
- token.Register(() =>
- {
- Console.WriteLine("Canceled");
- });
- Console.WriteLine("Press enter to cancel task...");
- Console.ReadKey();
- tokenSource.Cancel();
- Console.ReadKey();//这句忘了加,程序退出了,看不到“Abort mission success!“这个提示
这里开启了一个Task,并给token注册了一个方法,输出一条信息,然后执行ReadKey开始等待用户输入,用户点击回车后,执行tokenSource.Cancel方法,取消任务。其输出结果如下:
好了,今天先说道这里,明天继续讲task,接下来该说说task的异常处理和其他的一些用法,如果喜欢可以关注我,一有更新会马上通知你。
作者:雲霏霏
博客地址:http://www.cnblogs.com/yunfeifei/
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权,贴子请以现状保留,转载时必须保留此段声明,且在文章页面明显位置给出原文连接。
5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task的更多相关文章
- 5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
- 5天玩转C#并行和多线程编程 —— 第四天 Task进阶
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
- 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
- 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
- 5天玩转C#并行和多线程编程
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
- 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 转载 https://www.cnblogs.com/yunfeifei/p/3993401.html
5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...
- C#并行和多线程编程
5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线 ...
- C++多线程编程(三)线程间通信
多线程编程之三——线程间通讯 作者:韩耀旭 原文地址:http://www.vckbase.com/document/viewdoc/?id=1707 七.线程间通讯 一般而言,应用程序中的一个次要线 ...
- Java多线程编程核心技术(三)多线程通信
线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体.线程间的通信就是成为整体的必用方案之一,可以说,使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时 ...
随机推荐
- 20145318 GDB调试汇编堆栈分析
20145318 GDB调试汇编堆栈分析 代码 #include<stdio.h> short addend1 = 1; static int addend2 = 2; const sta ...
- C#之不借助第三变量交换两变量值
源码: 1 2 3 4 5 int n1=10, n2=20; n1 = n1 - n2; // -10 n2 = n1 + n2; // 10 n1 = n2 - n1 ...
- 为什么我坚持学习C语言?
c语言令无数人头疼,有意躲避! linux操作系统有c: glibc的c运行时库有c: mysql有c: php的底层有c: nosql层有c: 甚至其他的curl.nginx等等都有c: 每当问题追 ...
- socket网络间通信初识
NSOperation: 1. 指定同一时间最大执行的操作数 queue.max…… 2. 设定队列中的任务时间的依赖关系 task1 依赖于 task2: task2 —> task1 3. ...
- Windows 商店应用中使用 Office 365 API Tools
本篇我们介绍一个API 工具,用于在 Windows Store App 中使用 Office 365 API. 首先来说一下本文的背景: 使用 SharePoint 做过开发的同学们应该都知道,Sh ...
- 基于VC的ACM音频编程接口压缩Wave音频(三)
(三)音 频 数 据 的 压 缩 下 面 说 明 使 用 CODEC 实 现 音 频 压 缩 的 过 程:假 设 源 信 号 为8K 采 样.16bits PCM 编 码. 单 声 道. 长 度 为1 ...
- Java 后台获取当前时间
Calendar c = Calendar.getInstance();//可以对每个时间域单独修改 int year = c.get(Calendar.YEAR); int month = c.ge ...
- sizeof和指针
对于sizeof(类或对象)这种形式,如classA1{ public: int a; static int b;A1();~A1();}sizeof计算类或对象在栈中分配的大小,类或对象的静态变量存 ...
- 装个蒜。学习下dispatch queue
dispatch queue的真髓:能串行,能并行,能同步,能异步以及共享同一个线程池. 接口: GCD是基于C语言的APT.虽然最新的系统版本中GCD对象已经转成了Objective-C对象,但AP ...
- JavaScript 基础第七天(DOM的开始)
一.引言 JavaScript的内容分为三个部分,这三个部分分别是ECMAScript.DOM.BOM三个部分组成.所谓ECMAScript就是JavaScript和核心基础语法,DOM是文档对象模型 ...