原文:8天玩转并行开发——第二天 Task的使用

在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于

“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别?

1:任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。

2:任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小

的开销和精确的控制。

一:Task

1. 最简单的使用

开启task有两种方式:

<1> 实例化Task

1    //第一种方式开启
2 var task1 = new Task(() =>
3 {
4 Run1();
5 });

<2>从工厂中创建

1   var task2 = Task.Factory.StartNew(() =>
2 {
3 Run2();
4 });

是的,同样两种方法都可以创建,我们肯定会想两者是不是多多少少有点区别呢?好的,下面我们举个例子看分晓。

 1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //第一种方式开启
12 var task1 = new Task(() =>
13 {
14 Run1();
15 });
16
17 //第二种方式开启
18 var task2 = Task.Factory.StartNew(() =>
19 {
20 Run2();
21 });
22
23 Console.WriteLine("调用start之前****************************\n");
24
25 //调用start之前的“任务状态”
26 Console.WriteLine("task1的状态:{0}", task1.Status);
27
28 Console.WriteLine("task2的状态:{0}", task2.Status);
29
30 task1.Start();
31
32 Console.WriteLine("\n调用start之后****************************");
33
34 //调用start之前的“任务状态”
35 Console.WriteLine("\ntask1的状态:{0}", task1.Status);
36
37 Console.WriteLine("task2的状态:{0}", task2.Status);
38
39 //主线程等待任务执行完
40 Task.WaitAll(task1, task2);
41
42 Console.WriteLine("\n任务执行完后的状态****************************");
43
44 //调用start之前的“任务状态”
45 Console.WriteLine("\ntask1的状态:{0}", task1.Status);
46
47 Console.WriteLine("task2的状态:{0}", task2.Status);
48
49 Console.Read();
50 }
51
52 static void Run1()
53 {
54 Thread.Sleep(1000);
55 Console.WriteLine("\n我是任务1");
56 }
57
58 static void Run2()
59 {
60 Thread.Sleep(2000);
61 Console.WriteLine("我是任务2");
62 }
63 }

①:从图中可以看出两种task实例的简略生命周期。

Created:表示默认初始化任务,但是我们发现“工厂创建的”实例直接跳过。

WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。

RanToCompletion:任务执行完毕。

②:我们发现task的使用跟Thread很相似,就连waitAll的方法使用也一样,刚才也说了,任务是架构在线程之上,那么我们用VS里面的

“并行任务”看一看,快捷键Ctrl+D,K,或者找到“调试"->"窗口“->"并行任务“,我们在WaitAll方法处插入一个断点,最终我们发现

任务确实托管给了线程。

2. 取消任务

我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗? 当然我们可以做到。

在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的时候传入此参数,就可以将主线程和任务相

关联,然后在任务中设置“取消信号“叫做ThrowIfCancellationRequested来等待主线程使用Cancel来通知,一旦cancel被调用。task将会

抛出OperationCanceledException来中断此任务的执行,最后将当前task的Status的IsCanceled属性设为true。看起来是不是很抽象,

没关系,上代码说话。

 1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using System.Diagnostics;
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 var cts = new CancellationTokenSource();
10 var ct = cts.Token;
11
12 Task task1 = new Task(() => { Run1(ct); }, ct);
13
14 Task task2 = new Task(Run2);
15
16 try
17 {
18 task1.Start();
19 task2.Start();
20
21 Thread.Sleep(1000);
22
23 cts.Cancel();
24
25 Task.WaitAll(task1, task2);
26 }
27 catch (AggregateException ex)
28 {
29 foreach (var e in ex.InnerExceptions)
30 {
31 Console.WriteLine("\nhi,我是OperationCanceledException:{0}\n", e.Message);
32 }
33
34 //task1是否取消
35 Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled);
36 Console.WriteLine("task2是不是被取消了? {0}", task2.IsCanceled);
37 }
38
39 Console.Read();
40 }
41
42 static void Run1(CancellationToken ct)
43 {
44 ct.ThrowIfCancellationRequested();
45
46 Console.WriteLine("我是任务1");
47
48 Thread.Sleep(2000);
49
50 ct.ThrowIfCancellationRequested();
51
52 Console.WriteLine("我是任务1的第二部分信息");
53 }
54
55 static void Run2()
56 {
57 Console.WriteLine("我是任务2");
58 }
59 }

从图中可以看出

①:Run1中的Console.WriteLine("我是任务1的第二部分信息"); 没有被执行。

②:Console.WriteLine("task1是不是被取消了? {0}", task1.IsCanceled); 状态为True。

也就告诉我们Run1中途被主线程中断执行,我们coding的代码起到效果了。

3. 获取任务的返回值

我们以前写线程的时候注册的方法一般都是void类型,如果主线程要从工作线程中获取数据一般采用的手段是“委托+事件”的模式,然而

在Task中有两种方式可以解决。

<1>  现在我们的实例化是采用Task<TResult>的形式,其中TResult就是当前task执行后返回的结果,下面举得例子是t2任务获取

t1的执行结果。

 1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //执行task1
12 var t1 = Task.Factory.StartNew<List<string>>(() => { return Run1(); });
13
14 t1.Wait();
15
16 var t2 = Task.Factory.StartNew(() =>
17 {
18 Console.WriteLine("t1集合中返回的个数:" + string.Join(",", t1.Result));
19 });
20
21 Console.Read();
22 }
23
24 static List<string> Run1()
25 {
26 return new List<string> { "1", "4", "8" };
27 }
28 }

<2>采用ContinueWith方法,很有意思,现在我们将上面的方法改造一下。

 1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6
7 class Program
8 {
9 static void Main(string[] args)
10 {
11 //执行task1
12 var t1 = Task.Factory.StartNew<List<string>>(() => { return Run1(); });
13
14 var t2 = t1.ContinueWith((i) =>
15 {
16 Console.WriteLine("t1集合中返回的个数:" + string.Join(",", i.Result));
17 });
18
19 Console.Read();
20 }
21
22 static List<string> Run1()
23 {
24 return new List<string> { "1", "4", "8" };
25 }
26 }

4:ContinueWith结合WaitAll来玩一把

当这两者结合起来,我们就可以玩一些复杂一点的东西,比如说现在有7个任务,其中t1需要串行,t2-t3可以并行,t4需要串行,t5-t6并行,

t7串行。

好了,我们上一下代码说话,下面代码没有实际意思,纯属演示。

 1 using System;
2 using System.Threading;
3 using System.Threading.Tasks;
4 using System.Diagnostics;
5 using System.Collections.Generic;
6 using System.Collections.Concurrent;
7
8 class Program
9 {
10 static void Main(string[] args)
11 {
12 ConcurrentStack<int> stack = new ConcurrentStack<int>();
13
14 //t1先串行
15 var t1 = Task.Factory.StartNew(() =>
16 {
17 stack.Push(1);
18 stack.Push(2);
19 });
20
21 //t2,t3并行执行
22 var t2 = t1.ContinueWith(t =>
23 {
24 int result;
25
26 stack.TryPop(out result);
27 });
28
29 //t2,t3并行执行
30 var t3 = t1.ContinueWith(t =>
31 {
32 int result;
33
34 stack.TryPop(out result);
35 });
36
37 //等待t2和t3执行完
38 Task.WaitAll(t2, t3);
39
40
41 //t4串行执行
42 var t4 = Task.Factory.StartNew(() =>
43 {
44 stack.Push(1);
45 stack.Push(2);
46 });
47
48 //t5,t6并行执行
49 var t5 = t4.ContinueWith(t =>
50 {
51 int result;
52
53 stack.TryPop(out result);
54 });
55
56 //t5,t6并行执行
57 var t6 = t4.ContinueWith(t =>
58 {
59 int result;
60
61 //只弹出,不移除
62 stack.TryPeek(out result);
63 });
64
65 //临界区:等待t5,t6执行完
66 Task.WaitAll(t5, t6);
67
68 //t7串行执行
69 var t7 = Task.Factory.StartNew(() =>
70 {
71 Console.WriteLine("当前集合元素个数:" + stack.Count);
72 });
73
74 Console.Read();
75 }
76 }

并行开发 2.plink的更多相关文章

  1. [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下)

    [.net 面向对象程序设计进阶] (24) 团队开发利器(三)使用SVN多分支并行开发(下) 本篇导读: 接上篇继续介绍SVN的高级功能,即使用分支并行开发.随着需求的不断变更,新功能的增加.特别是 ...

  2. .NET下的并行开发(案例代码)

    以下主要是通过一个报表处理程序来说明并行开发的方式.对于数据冲突和共享,可以通过对象数组解决.设计到并行的核心代码已用红色标出.在并行程序的处理上,需要把原来串行的子公司变成一个一个类的对象,让所有的 ...

  3. .NET下的并行开发

    并行开发一直是程序员在开发项目中遇到的一道坎,但为了迎合硬件的升级,面对高端多核的处理器,并行编程势在必行.在.NET平台下的开发支持并行模式,下面用一个实际项目说明并行的高效率和神奇之处. 在优化中 ...

  4. 【翻译】CEDEC2014跨世代多平台并行开发PS4版如龙维新开发的一年

    本篇PPT讲述的是如龙4的开发过程中,集中在PS3和PS4并行开发中所遇到和解决的一些问题.如64位指针,DX9向DX11移植API的问题,以及在PS4上使用并行渲染在1080P下让FPS达到60等. ...

  5. 并行开发——Parallel的使用 -摘自网络

    随着多核时代的到来,并行开发越来越展示出它的强大威力,像我们这样的码农再也不用过多的关注底层线程的实现和手工控制, 要了解并行开发,需要先了解下两个概念:“硬件线程”和“软件线程”. 1. 硬件线程 ...

  6. 8天玩转并行开发——第一天 Parallel的使用

    转自:http://www.cnblogs.com/huangxincheng/archive/2012/04/02/2429543.html 随着多核时代的到来,并行开发越来越展示出它的强大威力,像 ...

  7. C# 并行开发总结

    本文内容 均参考自 <C#并行高级编程> TPL 支持 数据并行(有大量数据要处理,必须对每个数据执行同样的操作, 任务并行(有好多可以并发运行的操作),流水线(任务并行和数据并行的结合体 ...

  8. 并行开发学习随笔1——plinq并行

    这两天在看园友的文章 <8天玩转并行开发——第三天 plinq的使用> 对里面的第一个实例亲手实践了一下,发现了一点有意思的事情. 测试环境:.net 4.5 64位(如果是32位的,测试 ...

  9. 8天玩转并行开发——第三天 plinq的使用

    原文 8天玩转并行开发——第三天 plinq的使用 相信在.net平台下,我们都玩过linq,是的,linq让我们的程序简洁优美,简直玩的是爱不释手,但是传统的linq只是串行代码,在并行的 年代如果 ...

随机推荐

  1. 详解webpack4打包--新手入门(填坑)

    注意,这个dev和build好像在哪儿见过??对了, 刚刚才在package.json里配置的“scripts”这一项的值就有“dev”和“build”.对,一点都不错,就是这2个值,这2个值代表的是 ...

  2. linux权限管理—基本权限

    目录 Linux权限管理-基本权限 一.权限的基本概述 二.权限修改命令chmod 三.基础权限设置案例 四.属主属组修改命令chown Linux权限管理-基本权限 一.权限的基本概述 1.什么是权 ...

  3. 记录cobbler报错

    出现下面这个错误解决方法 httpd does not appear to be running and proxying cobbler, or SELinux is in the way. Ori ...

  4. Thinkphp 请求和响应

    一. Request对象获取方法 1. request() 助手函数获取 2. think\Request 类获取 3.利用框架注入Request对象  Request方法时单利方法 在think框架 ...

  5. CGfsb

    这里补充一下%n是代表向参数赋值打印的字符个数 例如printf("AAAA%n",&a); 代表的是向a写入4 printf("AAAA%1n", & ...

  6. C指针,&,*,指针的指针

    C指针: 指向变量的地址,想象成房间号 &: 取地址符号 *:间接访问符号, 访问p所存地址的内容 #include <iostream> int main(int argc, c ...

  7. swift中为什么要创造出可选型?

    (1)因为nil这个东西,swift中没有就是没有.  Int? 叫 整型可选型,如果不提前声明,直接赋值变量 nil会报错 . 可以将Int赋值给Int?   ,但是不能将Int?赋值给Int . ...

  8. c#类的定义,c#中的关健字,C#标识符

    什么是类:一种数数据结构,存储数据成员,方法成员,和其它的内容,便 于方便 谳用C#语法: class 类名{ //TODO} C#中关键字(小写)不能作为方法名,类名,命名空间名等, static ...

  9. php array_combine()函数 语法

    php array_combine()函数 语法 作用:通过合并两个数组来创建一个新数组,其中的一个数组是键名,另一个数组的值为键值.dd马达价格 语法:array_combine(keys,valu ...

  10. 最最简单的spring mvc + Maven项目

    首先配置pom文件,只需要引用三个jar包文件即可: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi ...