C#并行编程中的Parallel.Invoke
一、基础知识
并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴。所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务,这些任务不考虑互相的依赖和顺序。这样我们就可以使用很好的使用并行编程。但是我们都知道多核处理器的并行设计使用共享内存,如果没有考虑并发问题,就会有很多异常和达不到我们预期的效果。不过还好NET Framework4.0引入了Task Parallel Library(TPL)实现了基于任务设计而不用处理重复复杂的线程的并行开发框架。它支持数据并行,任务并行与流水线。核心主要是Task,但是一般简单的并行我们可以利用Parallel提供的静态类如下三个方法。
Parallel.Invoke 对给定任务实现并行开发
Parallel.For 对固定数目的任务提供循环迭代并行开发
parallel.Foreach 对固定数目的任务提供循环迭代并行开发
注意:所有的并行开发不是简单的以为只要将For或者Foreach换成Parallel.For与Parallel.Foreach这样简单。
PS:从简单的Invoke开始逐步深入探讨并行开发的主要知识点,也对自己学习过程中的积累做个总结,其中参考了博客园中的其他优秀博文
滴答的雨 异步编程:轻量级线程同步基元对象
首先感谢您,在我学习并行开发过程中,您的博文对我帮助很大。
二、Parallel.Invoke在并行中的使用
首先我们来看看它的两个重载方法:
public static void Invoke(params Action[] actions); public static void Invoke(ParallelOptions parallelOptions, params Action[] actions);
Invoke主要接受params的委托actions,比如我们要同时执行三个任务,我们可以这样利用
方式一 Parallel.Invoke(() => Task1(), () => Task2(), () => Task3()); 方式二 Parallel.Invoke(Task1, Task2, Task3); 方式三 Parallel.Invoke( () => { Task1(); }, Task2, delegate () { Task3(); console.write('do someting!');});
这样Invoke就简单实现了Task1,Task2,Task3的并行开发。下面我们用实例来说明他们的执行规则。以及两个重载方法的使用。
三 、Demo
1、 Demo 1:
public class ParallelInvoke{/// <summary>/// Invoke方式一 action/// </summary>public void Client1(){Stopwatch stopWatch = new Stopwatch();Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);stopWatch.Start();Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3"));stopWatch.Stop();Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);}private void Task1(string data){Thread.Sleep(5000);Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);}private void Task2(string data){Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);}private void Task3(string data){Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);}
}
执行运行后结果:
我们看到Invoke 执行Task三个方法主要有以下几个特点:
1、没有固定的顺序,每个Task可能是不同的线程去执行,也可能是相同的;
2、主线程必须等Invoke中的所有方法执行完成后返回才继续向下执行;这样对我们以后设计并行的时候,要考虑每个Task任务尽可能差不多,如果相差很大,比如一个时间非常长,其他都比较短,这样一个线程可能会影响整个任务的性能。这点非常重要
3、这个非常简单就实现了并行,不用我们考虑线程问题。主要Framework已经为我们控制好线程池的问题。
ps:如果其中有一个异常怎么办? 带做这个问题修改了增加了一个Task4.
2、 Demo2
public class ParallelInvoke{/// <summary>/// Invoke方式一 action/// </summary>public void Client1(){Stopwatch stopWatch = new Stopwatch();Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);stopWatch.Start();try{Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3"), delegate () { throw new Exception("我这里发送了异常"); });}catch (AggregateException ae){foreach (var ex in ae.InnerExceptions)Console.WriteLine(ex.Message);}stopWatch.Stop();Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);}}
主要看 delegate() { throw new Exception("我这里发送了异常");} 增加了这个委托Task3. 然后我们看结果:
这里我们发现即使有异常程序也会完成执行,而且不会影响其他Task的执行。
3、demo3 重载方法ParallelOptions 的使用。
理解ParallelOptions建议大家异步编程:轻量级线程同步基元对象 讲的非常详细。
主要理解两个参数:
CancellationToken 控制线程的取消
MaxDegreeOfParallelism 设置最大的线程数,有时候可能会跑遍所有的内核,为了提高其他应用程序的稳定性,就要限制参与的内核
下面从代码上看效果如何?
public class ParallelInvoke{// 定义CancellationTokenSource 控制取消readonly CancellationTokenSource _cts = new CancellationTokenSource();/// <summary>/// Invoke方式一 action/// </summary>public void Client1(){Console.WriteLine("主线程:{0}线程ID : {1};开始{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);var po = new ParallelOptions{CancellationToken = _cts.Token, // 控制线程取消MaxDegreeOfParallelism = 3 // 设置最大的线程数3,仔细观察线程ID变化};Parallel.Invoke(po, () => Task1("task1"), ()=>Task5(po), Task6);Console.WriteLine("主线程:{0}线程ID : {1};结束{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);}private void Task1(string data){Thread.Sleep(5000);Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);}
// 打印数字private void Task5(ParallelOptions po){Console.WriteLine("进入Task5线程ID : {0}", Thread.CurrentThread.ManagedThreadId);int i = 0;while (i < 100){// 判断是否已经取消if (po.CancellationToken.IsCancellationRequested){Console.WriteLine("已经被取消。");return;}Thread.Sleep(100);Console.Write(i + " ");Interlocked.Increment(ref i);}}/// <summary>/// 10秒后取消/// </summary>private void Task6(){Console.WriteLine("进入取消任务,Task6线程ID : {0}", Thread.CurrentThread.ManagedThreadId);Thread.Sleep(1000 * 10);_cts.Cancel();Console.WriteLine("发起取消请求...........");}
}
执行结果:
从程序结果我们看到以下特点:
1、程序在执行过程中线程数码不超过3个。
2、CancellationTokenSource/CancellationToken控制任务的取消。
四、总结
Parallel.Invoke 的使用过程中我们要注意以下特点:
1、没有特定的顺序,Invoke中的方法全部执行完才返回,但是即使有异常在执行过程中也同样会完成,他只是一个很简单的并行处理方法,特点就是简单,不需要我们考虑线程的问题。
2、如果在设计Invoke中有个需要很长时间,这样会影响整个Invoke的效率和性能,这个我们在设计每个task时候必须去考虑的。
3、Invoke 参数是委托方法。
4、当然Invoke在每次调用都有开销的,不一定并行一定比串行好,要根据实际情况,内核环境多次测试调优才可以。
5、异常处理比较复杂。
C#并行编程中的Parallel.Invoke相关推荐
- 多核心CPU并行编程中为什么要使用内存屏障 memory barriers / 内存栅栏 memory fence
文章目录 前言 现代Intel® CPU架构 指令集 CISC, RICS ... Intel各个时期的CPU微架构(microarchitecture)特点 P6 Family Microarchi ...
- 并行编程中的“锁”难题
在并行程序中,锁的使用会主要会引发两类难题:一类是诸如死锁.活锁等引起的多线程Bug:另一类是由锁竞争引起的性能瓶颈.本文将介绍并行编程中因为锁引发的这两类难题及其解决方案. 1. 用锁来防止数据竞跑 ...
- 【读书笔记】.Net并行编程高级教程--Parallel
一直觉得自己对并发了解不够深入,特别是看了<代码整洁之道>觉得自己有必要好好学学并发编程,因为性能也是衡量代码整洁的一大标准.而且在<失控>这本书中也多次提到并发,不管是计算机 ...
- 浅谈并行编程中的任务分解模式
并行编程使用线程来使得多个操作能够同时运行.并行编程主要包括应用程序中线程设计,开发和部署以及线程间相互协调和各自的操作. 在下文中我们将讨论怎样分割适合线程化大小的编程任务来多任务化一个应用程 ...
- java中两任务并行运行_Java并行编程中的“可调用”与“可运行”任务
java中两任务并行运行 当我们用Java编写多线程应用程序时,我们倾向于使用" Runnable "接口来实现线程类. 您的类必须简单地实现此接口并覆盖run方法. 对于琐碎的用 ...
- Java并行编程中的“可调用”与“可运行”任务
当我们用Java编写多线程应用程序时,我们倾向于使用" Runnable "接口来实现线程类. 您的类必须简单地实现此接口并覆盖run方法. 对于琐碎的用例,我们可以通过调用&qu ...
- C#中的多线程 - 并行编程 z
原文:http://www.albahari.com/threading/part5.aspx 专题:C#中的多线程 1并行编程Permalink 在这一部分,我们讨论 Framework 4.0 加 ...
- 如何运用并行编程Parallel提升任务执行效率
本文来自小易,[DoTNET技术圈]公众号已获得转载授权. <.NET并发变成实战>读后感:并行编程Parallel 手打目录: 一.前言 二.任务并行库(TPL)的介绍 三.Parall ...
- dnet 并行编程学习总结
.Net并行编程高级教程--Parallel http://www.cnblogs.com/stoneniqiu/p/4857021.html 一直觉得自己对并发了解不够深入,特别是看了<代码整 ...
最新文章
- java中冒号是什么意思_css样式表有哪些?css双冒号是什么意思
- java操作XML文件--读取内容
- 7、Docker容器数据卷volumes-from
- VTK:PolyData之InterpolateTerrain
- binutils工具集之---nm
- gsm,gprs,cmwap,cmnet,3g,TD-SCDMA,CDMA2000,WCDMA
- 三星framebuffer驱动代码分析
- linux常用命令之权限
- docker添加jar包_docker配置容器运行jar包
- mysql表设计要注意什么?
- 基础算法 —— 递推算法
- mysql分组后去重效率_mysql-mb6018ead621887的博客-51CTO博客
- jquery 2.0.3代码结构
- 阿里云云计算 23 VPC的基础架构
- 关于趋势科技防毒墙网络版的卸载
- 利用python实现端口扫描
- 【雪中漫步win7主题】情侣主题
- Daz导出模型的部件中英文对照
- 键盘输入一个长方形的两个边长,输出该长方形的周长、面积和对角线。要用到Math类提供的数学方法。
- Party Lemonade
热门文章
- c++Numerical string sort数字字符串排序的实现算法(附完整源码)
- C语言gauss elimination高斯消元法算法(附完整源码)
- C语言实现组织图kohone/topology算法(附完整源码)
- C++平衡二叉树(AVL树)
- C++是不是类型安全的?
- 经典C语言程序100例之二五
- C++11 - 返回类型后置
- AttributeError: module ‘tensorflow‘ has no attribute ‘placeholder‘
- IntelliJ IDEA 2017.01配置jdk和tomcat
- Hive基本操作,DDL操作(创建表,修改表,显示命令),DML操作(Load Insert Select),Hive Join,Hive Shell参数(内置运算符、内置函数)等