Parallel.For 你可能忽视的一个非常实用的重载方法

Parallel.For 你可能忽视的一个非常实用的重载方法,第1张

概述 说起Parallel.For大家都不会陌生,很简单,不就是一个提供并行功能的for循环吗? 或许大家平时使用到的差不多就是其中最简单的那个重载方法,而真实情况下Parallel.For里面有14个重载,而其中那些比较复杂的重载方法,或许还有同学还不知道怎么用呢~~~ 刚好我最近我有应用场景了,给大家介绍介绍,废话不多说,先给大家看一下这个并行方法的重载一览表吧。。。 一:遇到的场景     我遇到的场景是这样的,项目中有这样一个功能,这个功能需要根据多个维度对一组customerIDList进行筛选,最后求得多个维度所筛选出客户的并集,我举个例子:现有8个维度:1. 交易行为2.营销活动3.地区4.新老客户5.营销渠道6.客户属性7.客户分组8.商品每个维度都能筛选出一批customerid出来,然后对8组customerid求并集,这种场景很明显要提升性能的话,你必须要做并行处理,当然能够实现的方式有很多种,比如我定义8个task<T>,然后使用WaitAll等待一下,最后再累计每个Result的结果就可以了,代码如下:1 class Program2 {3 static void Main(string[] args)4 {5 List<string> rankList = Enum.GetNames(typeof(FilterType)).ToList();67 Task<HashSet<int>>[] tasks = new Task<HashSet<int>>[rankList.Count];89 var hashCustomerIDList = new HashSet<int>(); //求customerid的并集1011 for (int i = 0; i < tasks.Length; i++)12 {13 tasks[i] = Task.Factory.StartNew<HashSet<int>>((obj) =>14 {15 //业务方法,耗损性能中。。。16 var smallCustomerIDHash = GetXXXMethod(rankList[(int)obj]);1718 return smallCustomerIDHash;19 }, i);20 }2122 Task.WaitAll(tasks);2324 foreach (var task in tasks)25 {26 foreach (var item in task.Result)27 {28 hashCustomerIDList.Add(item);29 }30 }31 }3233 static HashSet<int> GetXXXMethod(string rank)34 {35 return new HashSet<int>();36 }3738 public enum FilterType39 {40 交易行为 = 1,41 营销活动 = 2,42 地区 = 4,43 新老客户 = 8,44 营销渠道 = 16,45 客户属性 = 32,46 客户分组 = 64,47 商品 = 12848 }49 }      上面的代码的逻辑还是很简单的,我使用的是Task<T>的模式,当然你也可以用void形式的Task,然后在里面lock代码的时候对hashCustomerIDList进行插入,实现起来也是非常简单的,我就不演示了,那下面的问题来了,有没有更爽更直接的方式,看人家看上去更有档次一点的方法,而且还要达到这种效果呢? 二:Parallel.For复杂重载 回到文章开头的话题,首先我们仔细分析一下下面这个复杂的重载方法。1 //2 // 摘要:3 // 执行具有线程本地数据的 for(在 Visual Basic 中为 For)循环,其中可能会并行运行迭代,而且可以监视和 *** 作循环的状态。4 //5 // 参数:6 // fromInclusive:7 // 开始索引(含)。8 //9 // toExclusive:10 // 结束索引(不含)。11 //12 // localInit:13 // 用于返回每个任务的本地数据的初始状态的函数委托。14 //15 // body:16 // 将为每个迭代调用一次的委托。17 //18 // localFinally:19 // 用于对每个任务的本地状态执行一个最终 *** 作的委托。20 //21 // 类型参数:22 // TLocal:23 // 线程本地数据的类型。24 //25 // 返回结果:26 // 包含有关已完成的循环部分的信息的结构。27 //28 // 异常:29 // T:System.ArgumentNullException:30 // body 参数为 null。- 或 -localInit 参数为 null。- 或 -localFinally 参数为 null。31 //32 // T:System.AggregateException:33 // 包含在所有线程上引发的全部单个异常的异常。34 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的使用贯穿着三个区域,所以大家一定要好好体会下面这几句代码1 TLocal tLocal = default(TLocal);23 tLocal = localInit();45 while(xxx<xxx){6 tLocal = bodyWithLocal(num5, parallelLoopState, tLocal);7 }8 localFinally(tLocal);       当你理解了tLocal具有累积foreach中的item结果之后,你就应该很明白下面这个body=>(item, loop, total) 和 finally => (total) 中total的含义了,对吧,当你明白了,然后大家可以看看下面这段代码,是不是用一个方法就搞定了原来需要分阶段实现的一个业务逻辑呢?1 class Program2 {3 static void Main(string[] args)4 {5 List<string> rankList = Enum.GetNames(typeof(FilterType)).ToList();67 var hashCustomerIDList = new HashSet<int>(); //求customerid的并集89 //并行计算 7个 维度的 总和10 Parallel.For(0, rankList.Count, () => { return new List<int>(); }, (item, loop, total) =>11 {12 //业务方法,耗损性能中。。。13 var smallCustomerIDHash = GetXXXMethod(rankList[item]);1415 total.AddRange(smallCustomerIDHash);1617 return total;18 }, (total) =>19 {20 lock (hashCustomerIDList)21 {22 foreach (var customerID in total)23 {24 hashCustomerIDList.Add(customerID);25 }26 }27 });28 }2930 static HashSet<int> GetXXXMethod(string rank)31 {32 return new HashSet<int>();33 }3435 public enum FilterType36 {37 交易行为 = 1,38 营销活动 = 2,39 地区 = 4,40 新老客户 = 8,41 营销渠道 = 16,42 客户属性 = 32,43 客户分组 = 64,44 商品 = 12845 }46 }   好了,本篇就先说这么多,希望这个具有并行累加器效果的Parallel.For能够给你带来一丝灵感~~~

 

下Parallel.For里面有14个重载,而其中那些比较复杂的重载方法,或许还有同学还不知道怎么用呢~~~ 刚好我最近我有应用场景了,给大家介绍介绍,废话不多说,

先给大家看一下这个并行方法的重载一览表吧。。。

     我遇到的场景是这样的,项目中有这样一个功能,这个功能需要根据多个维度对一组customerIDList进行筛选,最后求得多个维度所筛选出客户的并集,我举个

例子:现有8个维度:

每个维度都能筛选出一批customerID出来,然后对8组customerID求并集,这种场景很明显要提升性能的话,你必须要做并行处理,当然能够实现的方式有很多种,

比如我定义8个task,然后使用WaitAll等待一下,最后再累计每个Result的结果就可以了,代码如下:

Main( List<> rankList = Enum.Getnames( Task>>[] tasks = Task>> hashCustomerIDList = HashSet<>(); ( i = ; i < tasks.Length; i++ tasks[i] = Task.Factory.StartNew>>((obj) => smallCustomerIDHash = GetXXXMethod(rankList[( ( task ( item HashSet<> GetXXXMethod( HashSet<> 交易行为 = 营销活动 = 地区 = 新老客户 = 营销渠道 = 客户属性 = 客户分组 = 商品 = }

      上面的代码的逻辑还是很简单的,我使用的是Task的模式,当然你也可以用voID形式的Task,然后在里面lock代码的时候对hashCustomerIDList进行

插入,实现起来也是非常简单的,我就不演示了,那下面的问题来了,有没有更爽更直接的方式,看人家看上去更有档次一点的方法,而且还要达到这种效果呢?

 回到文章开头的话题,首先我们仔细分析一下下面这个复杂的重载方法。

ParallelLoopResult For( fromInclusive, toExclusive,Func localinit,Func<,ParallelLoopState,TLocal,TLocal> body,Action localFinally);

从上面的代码区域中看,你可以看到上面提供了5个参数,而最后意思的就是后面三个,如果你对linq的扩展方法比较熟悉的话,你会发现这个其实就是一个并行版本

的累加器(Aggregate) *** 作,因为他们都是具有三个区域:第一个区域就是初始化区域(localinit),就是累积之前的一个初始化 *** 作,第二个区域其实就是一个迭代

区域,说白了就是foreach/for循环,for循环之中,会把计算结果累计到当初初始化区域设置的变量中,第三个区域就是foreach/for之后的一个最终计算区,三者合起

来就是一个并行累加器,为了方便大家更好的理解,我就扒一下源码给大家看看:

 由于图太大,就截两张图了,大家一定要仔细体会一下这里面的tlocal变量,因为这个tlocal的使用贯穿着三个区域,所以大家一定要好好体会下面这几句代码

TLocal tLocal = tLocal = (xxx< tLocal = localFinally(tLocal);

      当你理解了tLocal具有累积foreach中的item结果之后,你就应该很明白下面这个body=>(item,loop,total) 和 finally => (total) 中total的含义了,

对吧,当你明白了,然后大家可以看看下面这段代码,是不是用一个方法就搞定了原来需要分阶段实现的一个业务逻辑呢?

Main( List<> rankList = Enum.Getnames( hashCustomerIDList = HashSet<>(); Parallel.For(,rankList.Count,() => { List<>(); },(item,total) => smallCustomerIDHash = },(total) => ( customerID HashSet<> GetXXXMethod( HashSet<> 交易行为 = 营销活动 = 地区 = 新老客户 = 营销渠道 = 客户属性 = 客户分组 = 商品 = }

   好了,本篇就先说这么多,希望这个具有并行累加器效果的Parallel.For能够给你带来一丝灵感~~~

总结

以上是内存溢出为你收集整理的Parallel.For 你可能忽视的一个非常实用的重载方法全部内容,希望文章能够帮你解决Parallel.For 你可能忽视的一个非常实用的重载方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1264099.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-08
下一篇 2022-06-08

发表评论

登录后才能评论

评论列表(0条)

保存