并行开发-Task
Task
对于多线程,经常使用的是Thread。在了解Task之前,如果要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,这就是Task,Task会比Thread具有更小的性能开销,Task是架构在Thread之上的就是说Task最终还是会抛给线程去做,并且任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。Task 类的表示单个操作不返回一个值,通常以异步方式执行,Task 对象是"基于任务的异步模式"首次引入.NET Framework 4 中。 因为由执行工作 Task 对象通常以异步方式执行在线程池线程上而不是以同步方式在主应用程序线程,可以使用Status属性,以及 IsCanceled, IsCompleted, 和 IsFaulted 属性,以确定任务的状态
创建任务的方式
使用实例化的ThreadFactory类
- var t1 = new TaskFactory().StartNew(() => Console.WriteLine("TaskFactory().StartNew"));
使用Task的静态属性Task静态属性Factory
- var t2 = Task.Factory.StartNew(() => Console.WriteLine("Task静态属性Factory"));
使用Task的构造函数(实例化Task对象时,任务不会立即执行,而是指定Created状态,通过Task.Start()方法启动)
- var t3 = new Task(() => Console.WriteLine("使用Task的构造函数"));
- t3.Start();
.Net4.5新增功能使用Task类的Run方法
- var t4 = Task.Run(() => Console.WriteLine("Task.Run"));
注意:使用Task.Run/Task.Factory.StartNew/new TaskFactory().StartNew()方法运行的任务会立即开始工作,无需显式地调用这些任务的Start方法
同步任务
任务不一定要使用线程池中的线程,也可以使用其他线程,任务也可以同步进行,以相同的线程作为主调线程

- static void Main(string[] args)
- {
- var t1 = new Task(() => TaskMethod("t1"));
- t1.Start();
- Console.WriteLine("主线程调用结束");
- Console.ReadKey();
- }
- public static void TaskMethod(string taskName)
- {
- Console.WriteLine("Task {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
- taskName, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
- }

- var t1 = new Task(() => TaskMethod("t1"));
- t1.RunSynchronously();
- Console.WriteLine("主线程调用结束");
- Console.ReadKey();
使用单独线程的任务
如果任务的代码应该长时间运行,就应该使用TaskCreationOptions.LongRuning告诉任务调度器创建一个新线程,而不是使用线程池中的线程,此时,线程可以不受线程池管理
- var t1 = new Task(TaskMethod, TaskCreationOptions.LongRunning);
- t1.Start();
Task生命周期
Created | 该任务已初始化,但尚未被计划 |
WaitingForActivation | 该任务正在等待 .NET Framework 基础结构在内部将其激活并进行计划 |
WaitingToRun | 该任务已被计划执行,但尚未开始执行 |
Running | 该任务正在运行,但尚未完成 |
WaitingForChildrenToComplete | 该任务已完成执行,正在隐式等待附加的子任务完成 |
RanToCompletion | 已成功完成执行的任务 |
Canceled |
该任务已通过对其自身的 CancellationToken 引发 OperationCanceledException 对取消进行了确认,此时该标记处于已发送信号状态;或者在该任务开始执行之前,已向该任务的 |
Faulted | 由于未处理异常的原因而完成的任务 |
Task任务控制
Task.Wait | task1.Wait();就是等待任务执行完成,task的状态变为Completed |
Task.WaitAll | 待所有的任务都执行完成 |
Task.WaitAny | 等待任何一个任务完成就继续向下执行 |
Task.ContinueWith | 第一个Task完成后自动启动下一个Task,实现Task的延续CancellationTokenSource |
CancellationTokenSource | 通过cancellation的tokens来取消一个Task |
获取任务返回值Task<TResult>

- static void Main(string[] args)
- {
- Task<string> t1 = new Task<string>(() => TaskMethod("t1"));
- t1.Start();
- Console.WriteLine(t1.Result);
- Console.ReadKey();
- }
- public static string TaskMethod(string taskName)
- {
- var result = string.Format("Task {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
- taskName, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
- return result;
- }

连续任务
在指定任务完成后调用另一个指定任务

- static void Main(string[] args)
- {
- Task<string> t1 = new Task<string>(() => TaskMethod1("t1"));
- Console.WriteLine("创建Task,状态为:{0}", t1.Status);
- t1.Start();
- Console.WriteLine("启动Task,状态为:{0}", t1.Status);
- Console.WriteLine(t1.Result);
- Console.WriteLine("创建Task,状态为:{0}", t1.Status);
- Task t2 = t1.ContinueWith(TaskMethod2);
- Console.ReadKey();
- }
- public static string TaskMethod1(string taskName)
- {
- var result = string.Format("TaskID {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
- taskName, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
- return result;
- }
- public static void TaskMethod2(Task t)
- {
- Console.WriteLine("Task {0} 运行在线程id为{1}的线程上。是否是线程池中线程?:{2}",
- t.Id, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
- }
- }

使用TaskContinuationOptions枚举的值可以指定连续任务只有在起始任务成功或失败结束时启动
- Task t2 = t1.ContinueWith(TaskMethod2, TaskContinuationOptions.NotOnCanceled);
嵌套Task
在Task内部创建Task,如果父任务在子任务之前结束,父任务状态就显示为WaitingForChilderenToComplete,所有的子任务也结束时,父任务的状态就改为RanToCompletion,如果使用TaskContinuationOptions枚举值创建子任务时会有不同结果,取消父任务也会取消子任务
Task的取消
- static void Main(string[] args)
{- CancellationTokenSource cts = new CancellationTokenSource();
- cts.CancelAfter();
- Task t1 = Task.Run(() =>
- {
- for (int i = ; i < ; i++)
- {
- if (cts.Token.IsCancellationRequested)
- {
- cts.Token.ThrowIfCancellationRequested();
- }
- else
- {
- Thread.Sleep();
- Console.WriteLine("任务t1,共执行30次,当前第{0}次", i);
- }
- }
- }, cts.Token);
- try
- {
- t1.Wait();
- }
- catch (AggregateException e)
- {
- foreach (var item in e.InnerExceptions)
- {
- Console.WriteLine(item);
- }
- }
- Console.ReadKey();
- }
工作原理
程序运行主线程创建->创建CancellationTokenSource对象->设置task在指定毫秒数后取消(这里是8000)->创建task并传入CancellationTokenSource对象生成的token,循环打印1~30,取消标记为true则抛出异常中止任务,false则正常输出,在输出前等待500毫秒(避免8000毫秒还没到任务就已经执行完成)->使用try/catch包裹t1.Wait()等待任务执行完成语句,并捕获处理异常.
这里任务在执行完15次的时候被取消.
多个Task
- static void Main(string[] args)
- {
- CancellationTokenSource cts = new CancellationTokenSource();
- cts.CancelAfter();
- Task[] tasks = new Task[];
- tasks[] = Task.Run(() =>
- {
- for (int i = ; i < ; i++)
- {
- if (cts.Token.IsCancellationRequested)
- {
- cts.Token.ThrowIfCancellationRequested();
- }
- else
- {
- Thread.Sleep();
- Console.WriteLine("任务t1,共执行30次,当前第{0}次", i);
- }
- }
- }, cts.Token);
- tasks[] = Task.Run(() =>
- {
- for (int i = ; i < ; i++)
- {
- if (cts.Token.IsCancellationRequested)
- {
- cts.Token.ThrowIfCancellationRequested();
- }
- else
- {
- Thread.Sleep();
- Console.WriteLine("任务t2,共执行50次,当前第{0}次", i);
- }
- }
- }, cts.Token);
- try
- {
- Task.WaitAll(tasks);
- }
- catch (AggregateException e)
- {
- foreach (var item in e.InnerExceptions)
- {
- Console.WriteLine(item);
- }
- }
- Console.ReadKey();
- }
使用Task.WaitAll()方法传递一个Task数组,并对其中多个Task的异常进行捕获处理
CancellationTokenSource.Token.Register()
- static void Main(string[] args)
- {
- CancellationTokenSource cts = new CancellationTokenSource();
- var token = cts.Token;
- cts.CancelAfter();
- token.Register(Callback);
- Task t1 = Task.Run(() =>
- {
- for (int i = ; i < ; i++)
- {
- if (token.IsCancellationRequested)
- {
- token.ThrowIfCancellationRequested();
- }
- else
- {
- Thread.Sleep();
- Console.WriteLine("任务t1,共执行30次,当前第{0}次", i);
- }
- }
- }, token);
- try
- {
- t1.Wait();
- }
- catch (AggregateException e)
- {
- foreach (var item in e.InnerExceptions)
- {
- Console.WriteLine(item);
- }
- }
- Console.ReadKey();
- }
- static void Callback()
- {
- Console.WriteLine("Register登记的任务取消回调函数");
- }
可使用 Register,向取消标记登记一个回调方法。应用程序调用 CancellationTokenSource 对象的 Cancel 方法时,这个回调就会运行。但是不能保证这个方法在什么时候执行,可 能在任务执行完自己的取消处理之前或之后,也可能在那个过程之中。
并行开发-Task的更多相关文章
- 8天玩转并行开发——第二天 Task的使用
原文 8天玩转并行开发——第二天 Task的使用 在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于 “任务的编程模型”所冲击, ...
- C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel
大家好,本次讨论的是C#中的并行开发,给力吧,随着并行的概念深入,哥也赶上这个潮流了,其实之前讨论C#的异步调用或者C#中BeginInvoke或者Invoke都已经涉及了部分本篇的内容. 参考书目: ...
- 并行开发 2.task
原文:8天玩转并行开发——第二天 Task的使用 在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于 “任务的编程模型”所冲击, ...
- C# 并行开发总结
本文内容 均参考自 <C#并行高级编程> TPL 支持 数据并行(有大量数据要处理,必须对每个数据执行同样的操作, 任务并行(有好多可以并发运行的操作),流水线(任务并行和数据并行的结合体 ...
- 8天玩转并行开发——第八天 用VS性能向导解剖你的程序
原文 8天玩转并行开发——第八天 用VS性能向导解剖你的程序 最后一篇,我们来说说vs的“性能向导",通常我们调试程序的性能一般会使用Stopwatch,如果希望更加系统的了解程序,我们就需 ...
- 并行开发-Paraller
并行开发的概念 并行开发要做的事情就是将任务分摊给硬件线程去并行执行来达到负载和加速,传统的代码都是串行的,就一个主线程,当我们为了实现加速而开了很多工作线程,这些工作线程就是软件线程 Paralle ...
- 转载 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等
随笔 - 353, 文章 - 1, 评论 - 5, 引用 - 0 三.并行编程 - Task同步机制.TreadLocal类.Lock.Interlocked.Synchronization.Conc ...
- 三、并行编程 - Task同步机制。TreadLocal类、Lock、Interlocked、Synchronization、ConcurrentQueue以及Barrier等
在并行计算中,不可避免的会碰到多个任务共享变量,实例,集合.虽然task自带了两个方法:task.ContinueWith()和Task.Factory.ContinueWhenAll()来实现任务串 ...
- NET中并行开发优化
NET中并行开发优化 让我们考虑一个简单的编程挑战:对大数组中的所有元素求和.现在可以通过使用并行性来轻松优化这一点,特别是对于具有数千或数百万个元素的巨大阵列,还有理由认为,并行处理时间应该与常规时 ...
随机推荐
- RobotFrameWork环境搭建(基于HTTP协议的接口自动化)
1. 前言 接着上一篇<RobotFramework框架系统课程介绍>,本篇主要介绍一下在基于RobotFramework框架开展接口自动化前,前期的环境如何搭建,正所谓”工欲善其事,必先 ...
- 【C++】C++中的数组
目录结构: contents structure [-] 一维数组 数组的定义和初始化 数组元素的访问 数组和指针 多维数组 数组是存放相同类型的容器,数组的大小确定不变,不能随意向数组中添加元素.因 ...
- Tomcat线程池的深入理解
1.工作机制: Tomcat启动时如果没有请求过来,那么线程数(都是指线程池的)为0: 一旦有请求,Tomcat会初始化minSpareThreads设置的线程数: 2.线程池作用: Tomcat的线 ...
- 用virsh console vhosts 卡住
[root@666 ok]# virsh list --all Id Name State ---------------------------------------------------- 1 ...
- curl 模拟请求
https://www.jianshu.com/p/7965c56c5a2e ********************************************* 一般情况下我们会在网页上请求后 ...
- linux下以‘-’开头的文件名
linux下以‘-’开头的文件名,cp.mv.rm.ls等对他都是无效的: [root@ha131 ~]# ll -plat.py ls:无效选项 -- . 请尝试执行"ls --help& ...
- [Git] 将本地分支与远程分支关联
. . . . . 在本地工程中添加Git,并将其与远程的空库关联起来,只需如下几步. 1. 创建空库 $ git init Initialized empty Git repository in D ...
- JVM系列——从菜鸟到入门
持续更新系列. 参考自<深入理解Java虚拟机>.<Java性能权威指南>.<分布式Java应用基础与实践>. Java的内存结构 JVM系列——运行时数据区 JV ...
- C语言 · 滑动解锁
题目:滑动解锁 滑动解锁是智能手机一项常用的功能.你需要在3x3的点阵上,从任意一个点开始,反复移动到一个尚未经过的"相邻"的点.这些划过的点所组成的有向折线,如果与预设的折线在图 ...
- WireShark如何抓取本地localhost的包
今天将自己的电脑既作为客户端又作为服务端进行一个程序的测试,想着用WireShark来抓包分析一下问题,但由于WireShark只能抓取经过电脑网卡的包,由于我是使用localhost或者127.0. ...