Task (任务):

为了更好地控制并行动作,可以使用 System.Threading.Task 命名空间下的 Task 类,任务表示完成的某个工作单元。这个工作单元可以在单独的线程中运行,也可以以同步的方式启动一个任务,这需要等待主调线程。任务不经可以获得一个抽象层,还可以对层线程进行很多控制。

官方链接:Task

1. 启动任务:

要启动任务,可以使用 TaskFactory类或 Task 类的构造函数和 Start() 方法。Task类的构造函数在创建任务上提供的灵活性较大。

在启动任务时,会创建 Task 类的一个 实例,利用 Action 或 Action< object > 委托(无参或带一个object 参数),可以指定运行的代码。如下:

     static object locker = new object();static void TaskMethod(object obj){lock (locker){Console.WriteLine(obj);Console.WriteLine("任务ID:{0}, 线程ID:{1}",Task.CurrentId==null?"no task":Task.CurrentId.ToString(),Thread.CurrentThread.ManagedThreadId);Console.WriteLine("是池线程?: {0}",Thread.CurrentThread.IsThreadPoolThread);Console.WriteLine("是后台线程?: {0}",Thread.CurrentThread.IsBackground);Console.WriteLine();}}

在上面方法中,打印出任务ID和线程ID,并且如果来自一个线程池,或线程是一个后台线程也要打印到控制台。把多条信息写入控制台的操作是使用 Lock 关键字和 locker 同步对象进行同步的。这样就可以并行调用 TaskMethod 方法。而多次写入控制台的操作也不会彼此交叉。否则,obj 可能有一个任务写入,而线程信息有另一个任务写入。

启动任务的几个不同方法:

1:使用线程池启动任务

        static void TaskUsingThreadPool(){var tf = new TaskFactory();Task t1 = tf.StartNew(TaskMethod, "使用任务工厂启动任务");Task t2 = Task.Factory.StartNew(TaskMethod, "通过工厂启动任务");Task t3 = new Task(TaskMethod, "使用任务构造函数并启动");t3.Start();Task t4 = Task.Run(() => TaskMethod("使用 Run 方法"));}

输出:

说明:
第一中方法是 实例化 TaskFactory 类,在其中把 TaskMethod 方法传递给 StartNew方法,就会立即启动任务。
第二中方法是 使用 Task 类的静态属性 Factory 来访问 TaskFactory,以及调用 StartNew() 方法。
第三中方式是使用 Task 类的构造函数。实例化 Task 对象时,任务不会立即运行,而是指定 Created 状态。接着调用 Task 类的 Start() 方法,来启动任务。
第四中方式是 .NET4.5 新增的,调用 Task 的 Run() 方法,立即启动任务。Run 方法没有可以传递 Action< object > 委托的重载版本,但是通过传递 Action 类型 的 lambda 表达式并在其实现中使用参数,可以模拟这种行为。

2:同步任务
任务不一定使用线程池中的线程,也可以使用其他线程。任务也可以同步运行,以相同的线程作为主掉线程。 如下:

 static void RunSynchronousTask(){TaskMethod("使用主线程");var t1 = new Task(TaskMethod, "同步运行");t1.RunSynchronously();}

输出:

说明:
首先在主线程上直接调用,然后再新建的 Task 上调用。主线程是一个前台线程,没有任务ID,也不是线程池中的线程。调用 RunSynchronously() 方法时,会使用相同的线程作为主调线程,但是如果以前没有创建任务就会创建一个新任务。

3:使用单独的线程任务

如果任务代码需要长时间运行,就应该使用 TaskCreationOptions.LongRunning 告诉任务调度器,创建一个新线程,而不是使用线程池中的线程。此时,线程可以不由线程池管理。当线程来自线程池时,任务调度器可以决定等待已经运行的任务完成,然后使用这个线程,而不是再线程池中创建一个新线程。对于长时间运行的线程,任务调度器会立即知道等待他们完成不是明智的做法。如下代码,创建了一个长时间运行的任务:

 static void LongRunningTask(){var t1 = new Task(TaskMethod, "长时间运行", TaskCreationOptions.LongRunning);t1.Start();}

输出:

可以看出,使用 TaskCreationOptions.LongRunning 不会使用线程池,而是创建新线程。

4. 连续的任务:

通过任务可以指定在任务完成后,应该开始运行另一个任务,例如,一个使用前一个任务的结果的新任务,如果前一个任务失败了,这个任务就应该执行一些清理工作。
任务处理程序或者不带参数,或者带一个对象参数,而连续处理程序有一个 Task 类型的参数,这里可以访问起始任务的相关信息。

 static void Main(string[] args){Task t1 = new Task(DoOnFirst);Task t2 = t1.ContinueWith(DoOnSecond);Task t3 = t1.ContinueWith(DoOnSecond);Task t4 = t3.ContinueWith(DoOnSecond);t1.Start();Console.ReadKey();}static void DoOnFirst(){Console.WriteLine("做一些任务,任务ID={0}",Task.CurrentId);Thread.Sleep(3000);}static void DoOnSecond(Task t){Console.WriteLine("任务 ID={0} 完成",t.Id);Console.WriteLine("这个任务ID={0}",Task.CurrentId);Console.WriteLine("做一些清理工作...");Thread.Sleep(3000);}

输出:

说明:
连续任务通过在任务上调用 ContinueWith() 方法来定义。 也可以使用 TaskFactory 类来定义。 t1.ContinueWith(DoOnSecond)方法表示,调用DoOnSecond() 方法的新任务应该在任务 t1 结束时立即启动。在一个任务结束时,可以启动多个任务,连续任务也可以有另一个连续任务。
无论前一个任务时如何结束的,后面的连续任务总是在前一个任务结束时启动。使用 TaskContinuationOptions 枚举中的值,可以指定,连续任务只有在起始任务成功(或失败)结束时启动。一些可能的值是 :

  • NotOnFaulted: 指定不应在延续任务前面的任务引发了未处理异常的情况下安排延续任务。 此选项对多任务延续无效。
  • OnlyOnFaulted: 指定只应在延续任务前面的任务引发了未处理异常的情况下才安排延续任务。 此选项对多任务延续无效。
  • OnlyOnCanceled: 指定只应在延续任务前面的任务已取消的情况下才安排延续任务。 此选项对多任务延续无效。
  • NotOnCanceled: 指定不应在延续任务前面的任务已取消的情况下安排延续任务。 此选项对多任务延续无效。
  • NotOnRanToCompletion: 指定不应在延续任务前面的任务已完成运行的情况下安排延续任务。 此选项对多任务延续无效。
  • OnlyOnRanToCompletion: 指定只应在延续任务前面的任务已完成运行的情况下才安排延续任务。 此选项对多任务延续无效。

等等。

例:

 Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);

Future - 任务结果:

任务结束时,可以把一些有用的状态信息写到共享对象中。这个共享对象必须是线程安全的。
另一个选项是使用返回某个结果的任务。这种任务也叫 Future(未来),因为它再将来返回一个结果 。 早期版本的 Task Parallel Library ( TPL ) 的类名也叫 Future ,现在它是 Task 类的一个泛型版本。使用这个类时,可以定义任务返回的结果的类型。
由任务调用来返回结果的方法可以声明为任何返回类型。如下实例方法:

 static void Main(string[] args){//LongRunningTask();var data = SampleData();var t1 =new  Task<double>(TaskWithRes, data);t1.Start();Console.WriteLine(t1.Result);t1.Wait();Console.WriteLine("任务返回结果: {0}",t1.Result);Console.ReadKey();}static double  TaskWithRes(object obj){List<int> data = (List<int>) obj;//求 集合中 数字的自然对数(底为 e)的值小于4 的元素 的 平均值var res = (from x in data.AsParallel()where Math.Log(x) < 4select x).Average();return res;}static IEnumerable<int> SampleData(){const int arraySize = 90000000;var r = new Random();return Enumerable.Range(0, arraySize).Select(x => r.Next(500)).ToList();}

输出:

任务的层次结构:

利用任务的连续性,可以在一个任务结束后启动另一个任务。任务也可以构成一个层次结构。一个任务启动一个新任务时,就启动了一个父/子层次结构。
如下代码:在父任务内部新建一个任务对象并启动。创建子任务的代码与创建父任务的代码相同,唯一的区别时这个任务是从另一个任务内部启动。

static void Main(string[] args){ParentAndChild();Console.ReadKey();}/// <summary>/// 子任务/// </summary>static void ChildTask(){Console.WriteLine("子任务");Thread.Sleep(3000);Console.WriteLine("子任务完成");}/// <summary>/// 父任务/// </summary>static void ParentTask(){Console.WriteLine("当前任务 ID={0}",Task.CurrentId);var child = new Task(ChildTask);child.Start();Thread.Sleep(1000);Console.WriteLine("父任务中启动子任务");}/// <summary>/// 调用任务/// </summary>static void ParentAndChild(){var parent = new Task(ParentTask);parent.Start();Thread.Sleep(1000);Console.WriteLine("父任务状态:{0}",parent.Status);Thread.Sleep(4000);Console.WriteLine("父任务状态:{0}", parent.Status);}

输出:

如果父任务在子任务之前结束,父任务的状态就会显示为 WaitingForChildrenToComplete 。所有的子任务也结束时,父任务的状态就变成 RunToCompletion。当然,如果父任务用 TaskCreationOptions 枚举中的 DetachedFromParent创建子任务时,这就无效了。

取消父任务,也会取消子任务。下一章重点讨论取消架构。

C#之:并行编程 -4相关推荐

  1. 多核时代,并行编程为何“臭名昭著”?

    作者 | Yan Gu 来源 | 转载自知乎用户Yan Gu [导读]随着计算机技术的发展,毫无疑问现代计算机的处理速度和计算能力也越来越强.然而细心的同学们可能早已注意到,从2005年起,单核的 C ...

  2. linux c 并行编程从入门到精通,VISUAL STUDIO 2010并行编程从入门到精通(微软技术丛书)...

    摘要: <微软技术丛书:Visual Studio2010并行编程从入门到精通>循序渐进,步骤式动手练习迅速帮助读者掌握并行编程的基础知识. <微软技术丛书:Visual Studi ...

  3. 用Hadoop进行分布式并行编程

    程序实例与分析 Hadoop 是一个实现了MapReduce 计算模型的开源分布式并行编程框架,借助于Hadoop, 程序员可以轻松地编写分布式并行程序,将其运行于计算机集群上,完成海量数据的计算.在 ...

  4. dnet 并行编程学习总结

    .Net并行编程高级教程--Parallel http://www.cnblogs.com/stoneniqiu/p/4857021.html 一直觉得自己对并发了解不够深入,特别是看了<代码整 ...

  5. 第一课-并行编程的几个概念

    为什么80%的码农都做不了架构师?>>>     为什么需要并行? 1. 业务要求 为了让用户有干好的产品使用感受,如ajax的异步请求,通过异步的执行让程序给用户带来的体验更加完美 ...

  6. python多线程和多进程——python并行编程实验

    工作中经常涉及到加速程序的运行,除了代码逻辑的优化,算法的优化之外,还经常使用的一招就是并发编程.至于python的并型编程这一块.说到并行编程,我们不得不谈线程和进程这两个概念: 进程:对于操作系统 ...

  7. Matlab并行编程函数cellfun arrayfun

    本篇blog针对两个函数cellfun和arrayfun对程序的加速写一些东西,方便大家调的一手好参数.之前的一篇blog<Matlab并行编程方法>在具体实现时可能有问题(下面会讲),而 ...

  8. 用 Hadoop 进行分布式并行编程, 第 3 部分 部署到分布式环境

    一 前言 在本系列文章的第一篇:用 Hadoop 进行分布式并行编程,第 1 部分: 基本概念与安装部署中,介绍了 MapReduce 计算模型,分布式文件系统 HDFS,分布式并行计算等的基本原理, ...

  9. 用 Hadoop 进行分布式并行编程, 第 2 部分 程序实例与分析

    前言 在上一篇文章:"用 Hadoop 进行分布式并行编程 第一部分 基本概念与安装部署"中,介绍了 MapReduce 计算模型,分布式文件系统 HDFS,分布式并行计算等的基本 ...

  10. 用 Hadoop 进行分布式并行编程, 第 1 部分 基本概念与安装部署

    Hadoop 简介 Hadoop 是一个开源的可运行于大规模集群上的分布式并行编程框架,由于分布式存储对于分布式编程来说是必不可少的,这个框架中还包含了一个分布式文件系统 HDFS( Hadoop D ...

最新文章

  1. 适合小团队协作、任务管理和进度跟踪的项目管理工具
  2. 深入理解C++内存管理
  3. 螺旋方阵 | 蛇形填数
  4. spring cloud学习进阶篇:Spring Cloud Sleuth + Zipkin 实现分布式跟踪解决方案
  5. 矩阵快速幂的最简单用法
  6. stm32 485和232可以用同一个串口吗_STM32的复用时钟何时开启?
  7. Jsp+Servlet+Mysql实现的酒店预定管理系统
  8. C#中级-Windows Service程序安装注意事项
  9. 浅谈SEO翻倍提升网站流量
  10. 云边端+AI,智慧仓储物流远程视频监控方案分析
  11. Instant Run(App加壳)
  12. 景深决定照相机什么特性_什么是景深?
  13. python 微信撤回消息
  14. SAP中销售订单流程及常用事务 Get the picture
  15. Python_从零开始学习_(27) 字符串
  16. 为K8S集群申请Let‘s Encrypt证书
  17. IT行业大致工作方向
  18. python框架sanic_Python Web框架Sanic框架初识
  19. 操作系统的概念、功能
  20. 移动聊天工具Kakao要开网络银行 牌照有望下月到手

热门文章

  1. ENSP配置 实例十 ACL配置
  2. C语言 const详解
  3. 修复miniblink一处内存泄漏的bug
  4. bert中文分类模型训练+推理+部署
  5. 用三元组存储稀疏矩阵及其快速转置
  6. LeNet5论文解读
  7. Codeforces 90B-African Crossword
  8. 中职学校计算机教学背景,简析中职学校计算机课堂有效教学
  9. oracle常见报错及解决方法
  10. VMware与USB3.0不解情缘(此文献给win7系统只有usb3.0口死活与虚拟机连不上的朋友们)