Parallel.For 你可能忽视的一个非常实用的重载方法
说起Parallel.For大家都不会陌生,很简单,不就是一个提供并行功能的for循环吗? 或许大家平时使用到的差不多就是其中最简单的那个重载方法,而真实情况
下Parallel.For里面有14个重载,而其中那些比较复杂的重载方法,或许还有同学还不知道怎么用呢~~~ 刚好我最近我有应用场景了,给大家介绍介绍,废话不多说,
先给大家看一下这个并行方法的重载一览表吧。。。
一:遇到的场景
我遇到的场景是这样的,项目中有这样一个功能,这个功能需要根据多个维度对一组customerIDList进行筛选,最后求得多个维度所筛选出客户的并集,我举个
例子:现有8个维度:
1. 交易行为
2.营销活动
3.地区
4.新老客户
5.营销渠道
6.客户属性
7.客户分组
8.商品
每个维度都能筛选出一批customerid出来,然后对8组customerid求并集,这种场景很明显要提升性能的话,你必须要做并行处理,当然能够实现的方式有很多种,
比如我定义8个task<T>,然后使用WaitAll等待一下,最后再累计每个Result的结果就可以了,代码如下:
class Program
{
static void Main(string[] args)
{
List<string> rankList = Enum.GetNames(typeof(FilterType)).ToList(); Task<HashSet<int>>[] tasks = new Task<HashSet<int>>[rankList.Count]; var hashCustomerIDList = new HashSet<int>(); //求customerid的并集 for (int i = ; i < tasks.Length; i++)
{
tasks[i] = Task.Factory.StartNew<HashSet<int>>((obj) =>
{
//业务方法,耗损性能中。。。
var smallCustomerIDHash = GetXXXMethod(rankList[(int)obj]); return smallCustomerIDHash;
}, i);
} Task.WaitAll(tasks); foreach (var task in tasks)
{
foreach (var item in task.Result)
{
hashCustomerIDList.Add(item);
}
}
} static HashSet<int> GetXXXMethod(string rank)
{
return new HashSet<int>();
} public enum FilterType
{
交易行为 = ,
营销活动 = ,
地区 = ,
新老客户 = ,
营销渠道 = ,
客户属性 = ,
客户分组 = ,
商品 =
}
}
上面的代码的逻辑还是很简单的,我使用的是Task<T>的模式,当然你也可以用void形式的Task,然后在里面lock代码的时候对hashCustomerIDList进行
插入,实现起来也是非常简单的,我就不演示了,那下面的问题来了,有没有更爽更直接的方式,看人家看上去更有档次一点的方法,而且还要达到这种效果呢?
二:Parallel.For复杂重载
回到文章开头的话题,首先我们仔细分析一下下面这个复杂的重载方法。
//
// 摘要:
// 执行具有线程本地数据的 for(在 Visual Basic 中为 For)循环,其中可能会并行运行迭代,而且可以监视和操作循环的状态。
//
// 参数:
// fromInclusive:
// 开始索引(含)。
//
// toExclusive:
// 结束索引(不含)。
//
// localInit:
// 用于返回每个任务的本地数据的初始状态的函数委托。
//
// body:
// 将为每个迭代调用一次的委托。
//
// localFinally:
// 用于对每个任务的本地状态执行一个最终操作的委托。
//
// 类型参数:
// TLocal:
// 线程本地数据的类型。
//
// 返回结果:
// 包含有关已完成的循环部分的信息的结构。
//
// 异常:
// T:System.ArgumentNullException:
// body 参数为 null。- 或 -localInit 参数为 null。- 或 -localFinally 参数为 null。
//
// T:System.AggregateException:
// 包含在所有线程上引发的全部单个异常的异常。
public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive, Func<TLocal> localInit, Func<int, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally);
从上面的代码区域中看,你可以看到上面提供了5个参数,而最后意思的就是后面三个,如果你对linq的扩展方法比较熟悉的话,你会发现这个其实就是一个并行版本
的累加器(Aggregate)操作,因为他们都是具有三个区域:第一个区域就是初始化区域(localInit),就是累积之前的一个初始化操作,第二个区域其实就是一个迭代
区域,说白了就是foreach/for循环,for循环之中,会把计算结果累计到当初初始化区域设置的变量中,第三个区域就是foreach/for之后的一个最终计算区,三者合起
来就是一个并行累加器,为了方便大家更好的理解,我就扒一下源码给大家看看:
由于图太大,就截两张图了,大家一定要仔细体会一下这里面的tlocal变量,因为这个tlocal的使用贯穿着三个区域,所以大家一定要好好体会下面这几句代码
TLocal tLocal = default(TLocal); tLocal = localInit(); while(xxx<xxx){
tLocal = bodyWithLocal(num5, parallelLoopState, tLocal);
}
localFinally(tLocal);
当你理解了tLocal具有累积foreach中的item结果之后,你就应该很明白下面这个body=>(item, loop, total) 和 finally => (total) 中total的含义了,
对吧,当你明白了,然后大家可以看看下面这段代码,是不是用一个方法就搞定了原来需要分阶段实现的一个业务逻辑呢?
class Program
{
static void Main(string[] args)
{
List<string> rankList = Enum.GetNames(typeof(FilterType)).ToList(); var hashCustomerIDList = new HashSet<int>(); //求customerid的并集 //并行计算 7个 维度的 总和
Parallel.For(, rankList.Count, () => { return new List<int>(); }, (item, loop, total) =>
{
//业务方法,耗损性能中。。。
var smallCustomerIDHash = GetXXXMethod(rankList[item]); total.AddRange(smallCustomerIDHash); return total;
}, (total) =>
{
lock (hashCustomerIDList)
{
foreach (var customerID in total)
{
hashCustomerIDList.Add(customerID);
}
}
});
} static HashSet<int> GetXXXMethod(string rank)
{
return new HashSet<int>();
} public enum FilterType
{
交易行为 = ,
营销活动 = ,
地区 = ,
新老客户 = ,
营销渠道 = ,
客户属性 = ,
客户分组 = ,
商品 =
}
}
好了,本篇就先说这么多,希望这个具有并行累加器效果的Parallel.For能够给你带来一丝灵感~~~
Parallel.For 你可能忽视的一个非常实用的重载方法的更多相关文章
- Linq下有一个非常实用的SelectMany方法,很多人却不会用
在平时开发中经常会看到有些朋友或者同事在写代码时会充斥着各种for,foreach,这种程式代码太多的话阅读性特别差,而且还显得特别累赘,其实在FCL中有很多帮助我们提高阅读感的方法,而现实中很多人不 ...
- Java中是否可以调用一个类中的main方法?
前几天面试的时候,被问到在Java中是否可以调用一个类中的main方法?回来测试了下,答案是可以!代码如下: main1中调用main2的主方法 package org.fiu.test; impor ...
- springmvc2 一个控制器写多个方法(非注解方式)
出处:http://blog.csdn.net/xuewenke/article/details/23895999 springmvc2 一个控制器写多个方法(非注解方式) 分类: spring 20 ...
- 一个Java复制目录的方法(递归)
/** * 将目标目录复制为指定目录(也可以用于复制文件) * @param src 源路径 * @param dest 目标路径 * @throws IOException */ public st ...
- JAVA中获得一个月最大天数的方法(备忘)
Calendar 类是一个抽象类,为日历字段之间的转换提供了一些方法.其中有一个重要方法 getActualMaximum ,该方法用于返回指定日历字段实际的最大值. 利用这个方法(Calendar. ...
- SpringMVC实现一个controller写多个方法
MultiActionController与ParameterMethodNameResolver在一个Controller类中定义多个方法,并根据使用者的请求来执行当中的某个方法,相当于Struts ...
- 一个类有两个方法,其中一个是同步的,另一个是非同步的; 现在又两个线程A和B,请问:当线程A访问此类的同步方法时,线程B是否能访问此类的非同步方法?
一个类有两个方法,其中一个是同步的,另一个是非同步的:现在又两个线程A和B,请问:当线程A访问此类的同步方法时,线程B是否能访问此类的非同步方法? 答案:可以 验证 package com.my.te ...
- [C语言]一个很实用的服务端和客户端进行TCP通信的实例
本文给出一个很实用的服务端和客户端进行TCP通信的小例子.具体实现上非常简单,只是平时编写类似程序,具体步骤经常忘记,还要总是查,暂且将其记下来,方便以后参考. (1)客户端程序,编写一个文件clie ...
- python每次处理一个字符的三种方法
python每次处理一个字符的三种方法 a_string = "abccdea" print 'the first' for c in a_string: print ord(c) ...
随机推荐
- npm 不是内部命令
最近办公室流行给电脑装win10系统,于是在重新装好电脑系统后,再次运行thinkjs项目的时候,就发现了之前做过的项目打不开了,待再确认问题出在哪里的时候,才发现”nodejs以及npm不是内部或者 ...
- WEB安全测试通常要考虑的测试点
1问题:没有被验证的输入测试方法: 数据类型(字符串,整型,实数,等)允许的字符集 最小和最大的长度是否允许空输入参数是否是必须的重复是否允许数值范围特定的值(枚举型)特定的模式(正则表达式) 2问题 ...
- 关于data-xxx属性大小写不敏感,不识别大写的几点总结
1.所有标签属性,没有大小写区分,都是小写,如:data-userID,在输出后会变成data-userid,前者只能获取到'undefined'. 2.dataset 自动把 - 转换为驼峰.类似的 ...
- Frogs
Problem Description There are m stones lying on a circle, and n frogs are jumping over them.The ston ...
- shell-3
每天学习一篇:利用跑代码或者空闲思考时间,启动环境时间等闲杂时间: http://blog.csdn.net/junjieguo/article/category/880326/2
- AWT与Swing的区别
AWT 是Abstract Window ToolKit (抽象窗口工具包)的缩写,这个工具包提供了一套与本地图形界面进行交互的接口.AWT 中的图形函数与操作系统所提供的图形函数之间有着一一对应的关 ...
- 从源码解析LinkedList集合
上篇文章我们介绍了ArrayList类的基本的使用及其内部的一些方法的实现原理,但是这种集合类型虽然可以随机访问数据,但是如果需要删除中间的元素就需要移动一半的元素的位置,效率低下.并且它内 ...
- 每天一个linux命令(27)--tar命令
通过SSH访问服务器,难免会要用到压缩,解压缩,打包,解包等,这时候 tar 命令就是必不可少的一个功能强大的工具.Linux 中最流行的 tar 是麻雀虽小,五脏俱全. tar 命令可以为Linux ...
- MYSQL中添加时间
1.在创建新记录和修改现有记录的时候都对这个数据列刷新:TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 2.在创建新记录 ...
- 启程、对C的继承
Cocoa和Object-c是苹果公司OS X和IOS操作系统的核心. .m扩展名表示文件使用的是Objective-c代码. 与C语言一样,Objective-c使用头文件来包含结构体.符号 ...