并行开发-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中并行开发优化 让我们考虑一个简单的编程挑战:对大数组中的所有元素求和.现在可以通过使用并行性来轻松优化这一点,特别是对于具有数千或数百万个元素的巨大阵列,还有理由认为,并行处理时间应该与常规时 ...
随机推荐
- MySQL 服务常用操作命令
1.MySQL 服务设置 在使用 mysql.server 命令之前需要将路径 /usr/local/mysql/support-files 添加到系统环境变量中. export PATH=$PATH ...
- IDEA使用笔记(十一)——好玩的类图结构
今天使用 IntelliJ IDEA 发现一个好玩的操作,尤其对于研究源码了解类的层级关系有非常大的帮助! 1:先看效果 1-1:HashSet的类图结构——继承什么类.实现什么接口一目了然 1-2: ...
- 网络协议学习(2)---IP地址
一.IPv4地址 IPv4地址为32bit地址,分为5类(ABCDE,这里不讨论特殊用途的D和E类). 通常我们八位一看,写成4个部分,例如:00000000 00000000 00000000 00 ...
- exp导出数据时丢表
友军发来消息,说使用exp导出某个schema的数据的时候,发现有些表没有导出来.因为一直没有使用exp的习惯,就使用exp\expdp再次导出一次,分析二者的日志,发现exp的确有些表没有导出. 问 ...
- 动态绑定事件到特定dom元素上,包含新增加的
$('body').on('click', 'a.detail-data', function (e) { //动态事件绑定 为body元素下所有的a.detail-data元素添加一个事件 包括新增 ...
- 关于 Docker Hub 上不能注册 Docker ID 的问题
1. 引言 我们中国大陆访问dockerhub的时候,想要注册一个dockerID,发现sign up按钮是灰色的,不能点击进行注册.这个时候通过点击右键"查看网页源代码"和&qu ...
- GridView不执行RowCommand事件
web.config里把viewstate禁用了.如果是的话在页面里单独开起来就好了. <%@ Page Title="" Language="C#" M ...
- Python3 字符串前面加u,r,b的含义
u/U:表示unicode字符串 不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行unicode编码. 一般英文字符在使用各种编码下, 基本都可以正常解析, 所以一般不带u:但是中文, ...
- make -j8以及linux下查看cpu的核数
# 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理CPU个数 X 每颗物理CPU的核数 X 超线程数 # 查看物理CPU个数 cat /proc/cpuinfo| ...
- vue的js文件中获取vue实例
1.main.js导出vue实例: var vue = new Vue({ el: '#app', router, components: { App }, template: '<App/&g ...