C# 4.0 并行计算部分
C# 4.0 并行计算部分
沿用微软的写法,System.Threading.Tasks.::.Parallel类,提供对并行循环和区域的支持。 我们会用到的方法有For,ForEach,Invoke。
一、简单使用
首先我们初始化一个List用于循环,这里我们循环10次。(后面的代码都会按这个标准进行循环)
- Program .Data = new List <int >();
- for (int i = 0; i < 10; i++)
- {
- Data.Add(i);
- }
下面我们定义4个方法,分别为for,foreach,并行For,并行ForEach。并测试他们的运行时长。
- /// <summary>
- /// 是否显示执行过程
- /// </summary>
- public bool ShowProcessExecution = false ;
- /// <summary>
- /// 这是普通循环for
- /// </summary>
- private void Demo1()
- {
- List <int > data = Program .Data;
- DateTime dt1 = DateTime .Now;
- for (int i = 0; i < data.Count; i++)
- {
- Thread .Sleep(500);
- if (ShowProcessExecution)
- Console .WriteLine(data[i]);
- }
- DateTime dt2 = DateTime .Now;
- Console .WriteLine("普通循环For运行时长:{0}毫秒。" , (dt2 - dt1).TotalMilliseconds);
- }
- /// <summary>
- /// 这是普通循环foreach
- /// </summary>
- private void Demo2()
- {
- List <int > data = Program .Data;
- DateTime dt1 = DateTime .Now;
- foreach (var i in data)
- {
- Thread .Sleep(500);
- if (ShowProcessExecution)
- Console .WriteLine(i);
- }
- DateTime dt2 = DateTime .Now;
- Console .WriteLine("普通循环For运行时长:{0}毫秒。" , (dt2 - dt1).TotalMilliseconds);
- }
- /// <summary>
- /// 这是并行计算For
- /// </summary>
- private void Demo3()
- {
- List <int > data = Program .Data;
- DateTime dt1 = DateTime .Now;
- Parallel .For(0, data.Count, (i) =>
- {
- Thread .Sleep(500);
- if (ShowProcessExecution)
- Console .WriteLine(data[i]);
- });
- DateTime dt2 = DateTime .Now;
- Console .WriteLine("并行运算For运行时长:{0}毫秒。" , (dt2 - dt1).TotalMilliseconds);
- }
- /// <summary>
- /// 这是并行计算ForEach
- /// </summary>
- private void Demo4()
- {
- List <int > data = Program .Data;
- DateTime dt1 = DateTime .Now;
- Parallel .ForEach(data, (i) =>
- {
- Thread .Sleep(500);
- if (ShowProcessExecution)
- Console .WriteLine(i);
- });
- DateTime dt2 = DateTime .Now;
- Console .WriteLine("并行运算ForEach运行时长:{0}毫秒。" , (dt2 - dt1).TotalMilliseconds);
- }
下面是运行结果:
这里我们可以看出并行循环在执行效率上的优势了。
结论1:在对一个数组内的每一个项做单独处理时,完全可以选择并行循环的方式来提升执行效率。
原理1:并行计算的线程开启是缓步开启的,线程数量1,2,4,8缓步提升。(不详,PLinq最多64个线程,可能这也是64)
二、 并行循环的中断和跳出
当在进行循环时,偶尔会需要中断循环或跳出循环。下面是两种跳出循环的方法Stop和Break,LoopState是循环状态的参数。
- /// <summary>
- /// 中断Stop
- /// </summary>
- private void Demo5()
- {
- List <int > data = Program .Data;
- Parallel .For(0, data.Count, (i, LoopState) =>
- {
- if (data[i] > 5)
- LoopState.Stop();
- Thread .Sleep(500);
- Console .WriteLine(data[i]);
- });
- Console .WriteLine("Stop执行结束。" );
- }
- /// <summary>
- /// 中断Break
- /// </summary>
- private void Demo6()
- {
- List <int > data = Program .Data;
- Parallel .ForEach(data, (i, LoopState) =>
- {
- if (i > 5)
- LoopState.Break();
- Thread .Sleep(500);
- Console .WriteLine(i);
- });
- Console .WriteLine("Break执行结束。" );
- }
执行结果如下:
结论2:使用Stop会立即停止循环,使用Break会执行完毕所有符合条件的项。
三、并行循环中为数组/集合添加项
上面的应用场景其实并不是非常多见,毕竟只是为了遍历一个数组内的资源,我们更多的时候是为了遍历资源,找到我们所需要的。那么请继续看。
下面是我们一般会想到的写法:
- private void Demo7()
- {
- List <int > data = new List <int >();
- Parallel .For(0, Program .Data.Count, (i) =>
- {
- if (Program .Data[i] % 2 == 0)
- data.Add(Program .Data[i]);
- });
- Console .WriteLine("执行完成For." );
- }
- private void Demo8()
- {
- List <int > data = new List <int >();
- Parallel .ForEach(Program .Data, (i) =>
- {
- if (Program .Data[i] % 2 == 0)
- data.Add(Program .Data[i]);
- });
- Console .WriteLine("执行完成ForEach." );
- }
看起来应该是没有问题的,但是我们多次运行后会发现,偶尔会出现错误如下:
这是因为List是非线程安全的类,我们需要使用System.Collections.Concurrent命名空间下的类型来用于并行循环体内。
类 | 说明 |
BlockingCollection<T> | 为实现 IProducerConsumerCollection<T> 的线程安全集合提供阻止和限制功能。 |
ConcurrentBag<T> | 表示对象的线程安全的无序集合。 |
ConcurrentDictionary<TKey, TValue> | 表示可由多个线程同时访问的键值对的线程安全集合。 |
ConcurrentQueue<T> | 表示线程安全的先进先出 (FIFO) 集合。 |
ConcurrentStack<T> | 表示线程安全的后进先出 (LIFO) 集合。 |
OrderablePartitioner<TSource> | 表示将一个可排序数据源拆分成多个分区的特定方式。 |
Partitioner | 提供针对数组、列表和可枚举项的常见分区策略。 |
Partitioner<TSource> | 表示将一个数据源拆分成多个分区的特定方式。 |
那么我们上面的代码可以修改为,加了了ConcurrentQueue和ConcurrentStack的最基本的操作。
- /// <summary>
- /// 并行循环操作集合类,集合内只取5个对象
- /// </summary>
- private void Demo7()
- {
- ConcurrentQueue <int > data = new ConcurrentQueue <int >();
- Parallel .For(0, Program .Data.Count, (i) =>
- {
- if (Program .Data[i] % 2 == 0)
- data.Enqueue(Program .Data[i]);//将对象加入到队列末尾
- });
- int R;
- while (data.TryDequeue(out R))//返回队列中开始处的对象
- {
- Console .WriteLine(R);
- }
- Console .WriteLine("执行完成For." );
- }
- /// <summary>
- /// 并行循环操作集合类
- /// </summary>
- private void Demo8()
- {
- ConcurrentStack <int > data = new ConcurrentStack <int >();
- Parallel .ForEach(Program .Data, (i) =>
- {
- if (Program .Data[i] % 2 == 0)
- data.Push(Program .Data[i]);//将对象压入栈中
- });
- int R;
- while (data.TryPop(out R))//弹出栈顶对象
- {
- Console .WriteLine(R);
- }
- Console .WriteLine("执行完成ForEach." );
- }
ok,这里返回一个序列的问题也解决了。
结论3:在并行循环内重复操作的对象,必须要是thread-safe(线程安全)的。集合类的线程安全对象全部在System.Collections.Concurrent命名空间下。
四、返回集合运算结果/含有局部变量的并行循环
使用循环的时候经常也会用到迭代,那么在并行循环中叫做 含有局部变量的循环 。下面的代码中详细的解释,这里就不啰嗦了。
- /// <summary>
- /// 具有线程局部变量的For循环
- /// </summary>
- private void Demo9()
- {
- List <int > data = Program .Data;
- long total = 0;
- //这里定义返回值为long类型方便下面各个参数的解释
- Parallel .For<long >(0, // For循环的起点
- data.Count, // For循环的终点
- () => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
- (i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前索引,LoopState是循环状态,subtotal为局部变量名
- {
- subtotal += data[i]; // 修改局部变量
- return subtotal; // 传递参数给下一个迭代
- },
- (finalResult) => Interlocked .Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
- );
- Console .WriteLine(total);
- }
- /// <summary>
- /// 具有线程局部变量的ForEach循环
- /// </summary>
- private void Demo10()
- {
- List <int > data = Program .Data;
- long total = 0;
- Parallel .ForEach<int , long >(data, // 要循环的集合对象
- () => 0, // 初始化局部变量的方法(long),既为下面的subtotal的初值
- (i, LoopState, subtotal) => // 为每个迭代调用一次的委托,i是当前元素,LoopState是循环状态,subtotal为局部变量名
- {
- subtotal += i; // 修改局部变量
- return subtotal; // 传递参数给下一个迭代
- },
- (finalResult) => Interlocked .Add(ref total, finalResult) //对每个线程结果执行的最后操作,这里是将所有的结果相加
- );
- Console .WriteLine(total);
- }
结论4:并行循环中的迭代,确实很伤人。代码太难理解了。
五、PLinq(Linq的并行计算)
上面介绍完了For和ForEach的并行计算盛宴,微软也没忘记在Linq中加入并行计算。下面介绍Linq中的并行计算。
4.0中在System.Linq命名空间下加入了下面几个新的类:
类 | 说明 |
ParallelEnumerable | 提供一组用于查询实现 ParallelQuery{TSource} 的对象的方法。这是 Enumerable 的并行等效项。 |
ParallelQuery | 表示并行序列。 |
ParallelQuery<TSource> | 表示并行序列。 |
原理2:PLinq最多会开启64个线程
原理3:PLinq会自己判断是否可以进行并行计算,如果不行则会以顺序模式运行。
原理4:PLinq会在昂贵的并行算法或成本较低的顺序算法之间进行选择,默认情况下它选择顺序算法。
在ParallelEnumerable中提供的并行化的方法
ParallelEnumerable 运算符 | 说明 |
AsParallel() | PLINQ 的入口点。指定如果可能,应并行化查询的其余部分。 |
AsSequential() | 指定查询的其余部分应像非并行 LINQ 查询一样按顺序运行。 |
AsOrdered() | 指定 PLINQ 应保留查询的其余部分的源序列排序,直到例如通过使用 orderby 子句更改排序为止。 |
AsUnordered() | 指定查询的其余部分的 PLINQ 不需要保留源序列的排序。 |
WithCancellation() | 指定 PLINQ 应定期监视请求取消时提供的取消标记和取消执行的状态。 |
WithDegreeOfParallelism() | 指定 PLINQ 应当用来并行化查询的处理器的最大数目。 |
WithMergeOptions() | 提供有关 PLINQ 应当如何(如果可能)将并行结果合并回到使用线程上的一个序列的提示。 |
WithExecutionMode() | 指定 PLINQ 应当如何并行化查询(即使默认行为是按顺序运行查询)。 |
ForAll() | 多线程枚举方法,与循环访问查询结果不同,它允许在不首先合并回到使用者线程的情况下并行处理结果。 |
Aggregate() 重载 | 对于 PLINQ 唯一的重载,它启用对线程本地分区的中间聚合以及一个用于合并所有分区结果的最终聚合函数。 |
下面是PLinq的简单代码
- /// <summary>
- /// PLinq简介
- /// </summary>
- private void Demo11()
- {
- var source = Enumerable .Range(1, 10000);
- //查询结果按source中的顺序排序
- var evenNums = from num in source.AsParallel().AsOrdered()
- where num % 2 == 0
- select num;
- //ForAll的使用
- ConcurrentBag <int > concurrentBag = new ConcurrentBag <int >();
- var query = from num in source.AsParallel()
- where num % 10 == 0
- select num;
- query.ForAll((e) => concurrentBag.Add(e * e));
- }
上面代码中使用了ForAll,ForAll和foreach的区别如下:
PLinq的东西很繁杂,但是都只是几个简单的方法,熟悉下方法就好了。
C# 4.0 并行计算部分的更多相关文章
- C# 4.0 Parallel
C# 4.0 并行计算部分 沿用微软的写法,System.Threading.Tasks.::.Parallel类,提供对并行循环和区域的支持. 我们会用到的方法有For,ForEach,Invo ...
- Other-Website-Contents.md
title: 本站目录 categories: Other sticky: 10 toc: true keywords: 机器学习基础 深度学习基础 人工智能数学知识 机器学习入门 date: 999 ...
- 灰度图像--图像分割 阈值处理之OTSU阈值
学习DIP第55天 转载请标明本文出处:***http://blog.csdn.net/tonyshengtan ***,出于尊重文章作者的劳动,转载请标明出处!文章代码已托管,欢迎共同开发:http ...
- ZAM 3D 制作简单的3D字幕 流程(二)
原地址:http://www.cnblogs.com/yk250/p/5663907.html 文中表述仅为本人理解,若有偏差和错误请指正! 接着 ZAM 3D 制作简单的3D字幕 流程(一) .本篇 ...
- ZAM 3D 制作3D动画字幕 用于Xaml导出
原地址-> http://www.cnblogs.com/yk250/p/5662788.html 介绍:对经常使用Blend做动画的人来说,ZAM 3D 也很好上手,专业制作3D素材的XAML ...
- 微信小程序省市区选择器对接数据库
前言,小程序本身是带有地区选着器的(网站:https://mp.weixin.qq.com/debug/wxadoc/dev/component/picker.html),由于自己开发的程序的数据是很 ...
- osg编译日志
1>------ 已启动全部重新生成: 项目: ZERO_CHECK, 配置: Debug x64 ------1> Checking Build System1> CMake do ...
- .NET 开源SqlServer ORM框架 SqlSugar 3.0 API
3.1.x ,将作为3.X系统的最后一个版本,下面将会开发 全新的功能 更新列表:https://github.com/sunkaixuan/SqlSugar/releases 优点: SqlSuga ...
- 并行计算提升32K*32K点(32位浮点数) FFT计算速度(4核八线程E3处理器)
对32K*32K的随机数矩阵进行FFT变换,数的格式是32位浮点数.将产生的数据存放在堆上,对每一行数据进行N=32K的FFT,记录32K次fft的时间. 比较串行for循环和并行for循环的运行时间 ...
随机推荐
- hdu5351 MZL's Border(规律题,java)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud MZL's Border Time Limit: 2000/1000 MS (Ja ...
- jquery 实现 隐藏交替同时记住以前隐藏的样式
/* * control menu show or hide(expand and collapse) */ var status = []; function menuOperation() { $ ...
- Java学习笔记--JDBC数据库的使用
参考 hu_shengyang的专栏 : http://blog.csdn.net/hu_shengyang/article/details/6290029 一. JDBC API中提供的常用数据库 ...
- 学习ReactNative笔记整理一___JavaScript基础
学习ReactNative笔记整理一___JavaScript基础 ★★★笔记时间- 2017-1-9 ★★★ 前言: 现在跨平台是一个趋势,这样可以减少开发和维护的成本.第一次看是看的ReactNa ...
- Java中的日期处理类
在Java中可以使用Date类和Calendar类来处理日期 但是Date类很多方法都过时了,推荐使用Canlendar类来处理日期,并对日期的格式化做了介绍.下面的部分将会逐一介绍 Date类 Ja ...
- 重启电脑提示Error:no such partition grub rescue
我的系统是Win7,在使用Ubuntu12.04自带的Wubi.exe安装双系统时,系统提示重新启动计算机,重启后电脑就停留在了黑屏界面并提示: error:no such partition gru ...
- CoFun 1613 单词连接
Description Stan有N个不同的单词,这天,Stan新结交的两个朋友来他这里玩,Stan作为主人,他需要送给他们单词,但由于Stan不能偏心,所以Stan给每个单词一个权值v_i,他需要他 ...
- 最新版AltiumDesignerSummer9下载+破解
下载地址:ed2k://|file|AltiumDesignerSummer9Build9.3.1.19182.7z|1875307483|e65d364bf987fb5dcfb81c081a1562 ...
- Qt HTTP请求同步调用
在Qt中,进行HTTP就行现在官方提倡使用QNetworkAccessManager,其和QNetworkRequest和QNetworkReply配合使用,来完成,其是只支持异步的操作.最近使用QM ...
- 在 Azure 虚拟机上部署反恶意软件解决方案
本博客文章由我和我的同事 Sung Hsueh 共同撰写,Sung Hsueh 是 Microsoft Azure 计算部负责安全事项的项目经理. 本博客文章为"虚拟机扩展程序"系 ...