Parallel的使用 之Parallel.forrech
首先一般Foreach,Parallel.For,Parallel.Foreach三种情况的效率问题,
要知道的是:Parallel.Foreach性能最优,其次 Parallel.For 最后 Foreach 但是这只是一般情况下,如果循环里面执行的代码块非常简单,执行时间特别短,那么性能最优的是Foreach ,原因很简单,因为 Parallel是需要创建线程的,会开销线程耗时间。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;using System.Threading.Tasks;
namespace 测试ParallelFor
{
class Program
{
/*
* 测试分析结果
* Parallel.For、Parallel.Foreach发挥出了平行运算的优势,将效率提高了接近一半左右。
*
* 测试总结
* 对于Parallel.For、Parallel.Foreach的使用应该要特别小心,
* 它们的优势是处理列表很长,且对列表内的元素进行很复杂的业务逻辑,且不会使用共享资源,
* 只针对自身的业务逻辑处理,方才能提升效率。
* 因为如果逻辑过于简单的话,创建线程的花费将大于业务执行的花费,得不偿失。
*/
static void Main(string[] args)
{
//产生测试资料
List<int> testData = new List<int>();
Random Rand = new Random();
//产生乱数列表
for (int i = 0; i < 1000000; i++)
{
testData.Add(Rand.Next(1000));
}
//打印正确结果
Console.WriteLine(testData.Sum());for (int i = 0; i < 5; i++)
{
Console.WriteLine();
TestFor(testData);
TestParallelFor(testData);
TestParallelForeach(testData);
}
Console.ReadKey();
}static void TestFor(List<int> testData)
{
DateTime time1 = DateTime.Now;
foreach (var item in testData)
{
item.ToString();
}
Console.WriteLine(string.Format("ForEach: t{0} in {1}", testData.Sum(), (DateTime.Now - time1).TotalMilliseconds));
}static void TestParallelFor(List<int> testData)
{
DateTime time1 = DateTime.Now;
Parallel.For(0, testData.Count, (i, loopState) =>
{
testData[i].ToString();
});
Console.WriteLine(string.Format("Parallel.For: t{0} in {1}", testData.Sum(), (DateTime.Now - time1).TotalMilliseconds));
}static void TestParallelForeach(List<int> testData)
{
//记录结果用
DateTime time1 = DateTime.Now;
Parallel.ForEach(testData, (item, loopState) =>
{
item.ToString();
});
Console.WriteLine(string.Format("Parallel.ForEach:t{0} in {1}", testData.Sum(), (DateTime.Now - time1).TotalMilliseconds));
}
}
}
实验2:Parallel的线程管理情况
测试结果:
Parallel会再最近的一个thread结束后,把该完成的ThreadId作为新的开辟的线程的Id,
源码:
实验3:最大线程数。线程是CPU进行调度的单位,进程是系统进程调度的单位。线程组成进程。设置平行运行最大最大线程数:2个;这样系统运行可控,不会造成高CPU的情况
结果及源码:
arallel的跳出循环以及终止循环。
Parallel.ForEach(list,new ParallelOptions(){ MaxDegreeOfParallelism=2}, (p, state1) =>
{
Invoke(p);
state1.Break();//Break用于根据条件过滤循环,Break不是Continue,不要搞混了!Break是执行完现有的迭代后跳出!
state1.Stop();//Stop方法用于退出Paraller循环,表示立刻退循环,cease=终止
return; //注意:不论是Break还是Stop方法,后面的return语句是必须的,否则当前循环体第13行的语句还是会被执行。
});
ParallelLoopState.Stop() 提供了退出循环的方法,这种方式要比其他两种方法更快。这个方法通知循环不要再启动执行新的迭代,并尽可能快的推出循环。
ParallelLoopState.IsStopped 属性可用来判定其他迭代是否调用了 Stop 方法。
Break不是Continue,不要搞混了!Break是执行完现有的迭代后跳出!
ParallelLoopState.Break() 通知循环继续执行本元素前的迭代,但不执行本元素之后的迭代。最前调用 Break 的起作用,并被记录到 ParallelLoopState.LowestBreakIteration 属性中
其他知识点:
未设置最大线程数的情况下:
1.为设置最大线程的情况下,TPL默认线程数为任务数(系统允许的情况下,设置ThreadPool.SetMaxThreads没有效果)。
2.TPL默认启动5个线程,任务数小于5的话,启动任务数个线程。
3.如果任务较多,TPL在初始化5个线程后,每隔100毫秒左右新增线程,直到达到最大线程数。如果新增线程的过程中有任务完成,那么就不会新增线程。
缺点:线程数无法控制,容易造成高CPU,系统失去响应。
举一个比较有实际意义的案例:
在数据库有会员4000万左右,现在需要把这些会员全部获取到,并且根据会员卡号查询其他相关表信息,来给各个会员打上标签,来指定一些分类营销 例如:给不用会员的使用习惯发不同的优惠券,推送短信,公众号推广,积分提醒 等等
如果我们用一个主线程直接for循环,由于每一个会员的属性都特别复杂,估计全部跑完要一个月时间,但是我们取得数据都是根据会员动态信息来分析的,当你全部完成打标签时,已打标签跟会员当前的特性已经不完全吻合了,那已经是一个月的数据,而且单线程这样跑,会出现很多意料之外的问题,所以这个方案显然是不满足需求的。
那么只能用多线程了。但是多线程方式很多,可以用 Parallel.ForEach
思路很简单: 会员里面是有主键ID的,我们这边是int类型,如果表设计的时候不是int类型,可以考虑其他字段,只要是唯一的,可排序的就可以,不一定是id。
1:查询数据库会员总数量
2:判断一下大概每个线程一次执行多少数量,例如200
3:那么大概遍历次数: int Labercount = (int)Math.Ceiling(Convert.ToDecimal(dtLabercount.Rows[0][0]) / 200);
4:定义3个数组,listobj是数组里面的数组,目的:subItemList需要放2个参数,第一个参数 就是顺序,第二个是 对低多少条数据,
var listObj = new List<List<string>>();
List<string> subItemList;
for (int i = Labercount; i < count; i++)
{
subItemList .Add((i).ToString());
subItemList .Add((200 + 200* i).ToString());
listObj.Add(subItemList);
subItemList .Clear();
}
这样 listObj里面的数量就是 4000万/200个数组
var option = new ParallelOptions { MaxDegreeOfParallelism = thread}; // 定义一个最大线程数:
var watch = Stopwatch.StartNew();
System.Threading.Tasks.Parallel.ForEach(listObj.AsParallel(), option, (parameter) =>
{
try
{
Console.WriteLine("时间: " + DateTime.Now + " 会员信息ID号为:" + Convert.ToInt32(parameter[0]) * 200 + " 段号数据");
//构造传递的参数
Hashtable hs = new Hashtable();
hs.Add("pageIndex", parameter[0]);
hs.Add("pageSize", 200);
DataTable dt = member.QueryMember(hs);
if (dt != null && dt.Rows.Count > 0)
{
--- insert 到分类表
DataTable dtdetail = GetMemberCategorydt(dt);
int result = cate.BulkCopyDataToDB(dtdetail);
}
}
catch (Exception ex)
{
//Logger.WriteAppError(ex, parameter[0] + "号段报错");
Console.WriteLine(ex+parameter[0] + "号段报错"+ ex.Message);
}
});
watch.Stop();
Console.WriteLine("更新记录完成,用时{0}\r\n", watch.Elapsed.TotalMinutes);
这里需要理解Parallel.ForEach里面的参数 :
1:第一个参数是数组,就是上面代码分析后得到的数组对象,每个里面都有会员的主键信息
2:第二个参数是设置线程最大数,如果不设置最大数,就会默认先创建5个,然后不停的新增线程数,知道最大为止,这是不可取的,因为并不是线程越多就越好。
3: (parameter) =>是拉姆达表达式的写法,里面就是 listObj数组的元素,当然这里的listObj里面还是数组,如果前面设计的是不那么复杂,直接是string或者int类型数据,那么parameter也就是string 或者int类型的数据。
4:根据这个数据 去会员表查询,这里做排序查询,相当于分页查询一下,根据parameter里面的参数,做查询,只是这里的分页不是排序的,而是无序的。这里每次查询就是小于等于200个会员信息。为什么是小于等于,因为我们无法保证里面的参数每一条在数据库都存在,
5:获取到会员卡号。根据会员卡号到其他关联表取数据分别做不同的分析
整个流程下来,4000万会员大概在3天左右全部跑完。勉强可以接受
再添加一个比较好理解的代码
class ParallelForeach
{
public void TestParllerlForeach()
{
var list = new List<int>(100);
for (int i = 0; i < 100; i++)
{
list.Add(i);
}
// 这里的p是遍历list的数据 ,但是不是排序的取,而且随机的取出, MaxDegreeOfParallelism是最大线程数量
Parallel.ForEach(list,new ParallelOptions() {MaxDegreeOfParallelism=2 }, (p) =>
{
InvokoForeach(p);
});
}
private void InvokoForeach(int i)
{
Console.WriteLine("------>当前ThreadId:" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
Console.WriteLine("<-------ThreadId:" + Thread.CurrentThread.ManagedThreadId + "完毕");
}
}
Parallel的使用 之Parallel.forrech相关推荐
- 27.垃圾收集器(Serial收集器、ParNew收集器、Parallel收集器、Parallel Old 收集器、CMS收集器、G1收集器、常用的收集器组合)
27.垃圾收集器 27.1.Serial收集器 27.2.ParNew收集器 27.3.Parallel收集器 27.4.Parallel Old 收集器 27.5.CMS收集器 27.6.G1收集器 ...
- java parallel.for作用_“Parallel.For”for Java?
我想最接近的事情是: ExecutorService exec = Executors.newFixedThreadPool(SOME_NUM_OF_THREADS); try { for (fina ...
- Parallel的使用 之Parallel.for
Paraller.For()方法类似于C#的for循环语句,也是多次执行一个任务.使用Paraller.For()方法,可以并行运行迭代,迭代的顺序没有定义. 在For()方法中,前两个参数是固定的, ...
- [深入学习C#]C#实现多线程的方式:使用Parallel类
简介 在C#中实现多线程的另一个方式是使用Parallel类. 在.NET4中 ,另一个新增的抽象线程是Parallel类 .这个类定义了并行的for和foreach的 静态方法.在为 for和 f ...
- C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel
大家好,本次讨论的是C#中的并行开发,给力吧,随着并行的概念深入,哥也赶上这个潮流了,其实之前讨论C#的异步调用或者C#中BeginInvoke或者Invoke都已经涉及了部分本篇的内容. 参考书目: ...
- oracle parallel 并行 设置 理解
引子:以前一直没太关注oracle并行这个特性.前几天一个兄弟碰到的一个问题,才让我觉得这个东西还是有很多需要注意的地方,有必要仔细熟悉下.其实碰到的问题不复杂: 类似如下的一条语句:insert i ...
- C# 4.0 新特性之并行运算(Parallel)
介绍 C# 4.0 的新特性之并行运算 Parallel.For - for 循环的并行运算 Parallel.ForEach - foreach 循环的并行运算 Parallel.Invoke - ...
- linux 并行计算命令,Linux下的并行神器——parallel
GNU Parallel是一个shell工具,为了在一台或多台计算机上并行的执行计算任务.本文简要介绍GNU Parallel的使用. 1.parallel 用法简介 Usage: parallel ...
- Oracle Hint 之 Parallel
强制启用oralce的多线程处理功能. 并行查询允许将一个sql select 语句划分为多个较小的查询,每个部分的查询并发的运行,然后将各个部分的结果组合起来,提供最终的结果,多用于全表扫描,索引全 ...
最新文章
- StoryBoard布局注意事项
- 心电图前波过多_心电图写着:T波倒置,就是心肌缺血吗?医生:不能如此草率...
- 回顾2018,计划2019
- Bapi-BAPI_GOODSMVT_CREATE【该物料不可能有库存记帐】
- Linux vim的w,q,!,/
- 岛国人气美少女竟然每晚跟 3 个人通宵打麻将?
- 用9种办法解决 JS 闭包经典面试题之 for 循环取 i
- [转载] python中list的方法有哪些_Python 列表(list)中的方法
- Phalcon调试大杀器之phalcon-debugbar安装
- oracle PL/SQL(procedure language/SQL)程序设计之异常(exception)
- 大势至服务器共享文件监控软件8.6,大势至服务器共享文件夹监控软件、局域网共享管理软件、局域网共享设置软件...
- 51单片机外部中断实例
- ubuntu下使用vscode编译调试yolov3
- android /data/system/dropbox,Android dropbox日志浅谈
- Python脚本把支付宝和微信账单数据转换成随手记APP的excel标准模板导入
- Storage of multidimensional arrays based on arbitrary tiling
- Vue + MathLive 实现数学公式可编辑
- 如何在以太坊上发行自己的代币
- GT sport真实赛道详解 - Brands Hatch | 伯蘭士赫治GP賽車場
- brew cask安装软件提示:Error: Unknown command: cask
热门文章
- input只能输入正整数,且第一个不能为0,不能输入小数点
- 老生常谈:微博,QQ,淘宝三种开发平台对比 笔记
- 用什么工具上架ios app
- arcgis几何修复有作用吗_ARCGIS几何修复使用技巧
- Oracle之SQL命中率
- Removing obsolete files from server... Could not clean server of obsolete files: 前言中不允许有内容。
- 企业信息化要解决的难题
- 电信aep平台是什么意思_杠杆股票配资平台米牛金融股票配资线上炒股配资公司:现代场内与场外配资是什么意思...
- 小米遥控车吉姆尼加装车灯~
- wordpress采集器-wordpress采集器安装下载教程