使用AsParallel()/ Parellel.ForEach()指导方针?
寻求一些利用AsParallel()
或者Parallel.ForEach()
来加快速度的build议。
见下面的方法(这个例子简化/混蛋)。
它需要一个像“美国,法国,亚太地区”这样的名单,其中“亚太地区”可能是另外50个“美国,法国,日本,美国,英国等等”的别名。 方法应采取“美国,法国,亚太地区”,并将其转换为“美国”,“法国”,以及所有“亚太地区”国家的名单。
private IEnumerable<string> Countries (string[] countriesAndAliases) { var countries = new List<string>(); foreach (var countryOrAlias in countriesAndAliases) { if (IsCountryNotAlias(countryOrAlias)) { countries.Add(countryOrAlias); } else { foreach (var aliasCountry in AliasCountryLists[countryOrAlias]) { countries.Add(aliasCountry); } } } return countries.Distinct(); }
是不是把这个并行化就像把它改变成下面那样简单? 使用AsParallel()
比这更多的细微差别? 我应该使用Parallel.ForEach()
而不是foreach
? 并行化foreach
循环时应使用什么经验法则?
private IEnumerable<string> Countries (string[] countriesAndAliases) { var countries = new List<string>(); foreach (var countryOrAlias in countriesAndAliases.AsParallel()) { if (IsCountryNotAlias(countryOrAlias)) { countries.Add(countryOrAlias); } else { foreach (var aliasCountry in AliasCountryLists[countryOrAlias].AsParallel()) { countries.Add(aliasCountry); } } } return countries.Distinct(); }
几点。
只写countriesAndAliases.AsParallel()
别名.AsParallel countriesAndAliases.AsParallel()
是没用的。 AsParallel()
成为并行执行后的Linq查询的一部分。 零件是空的,所以根本没用。
一般来说你应该使用Parallel.ForEach()
重新使用foreach
。 但要小心不是线程安全的代码! 你拥有了它。 你不能把它包装到foreach
因为List<T>.Add
本身不是线程安全的。
所以你应该这样做(对不起,我没有testing,但它编译):
return countriesAndAliases .AsParallel() .SelectMany(s => IsCountryNotAlias(s) ? Enumerable.Repeat(s,1) : AliasCountryLists[s] ).Distinct();
编辑 :
你必须确定另外两件事情:
-
IsCountryNotAlias
必须是线程安全的。 如果它是纯粹的function会更好。 - 同时,没有人会修改
AliasCountryLists
,因为字典不是线程安全的。 或者使用ConcurrentDictionary来确定。
有用的链接将帮助你:
并行编程模式:了解和应用.NET Framework 4的并行模式
在.NET 4编码指南中的并行编程
何时使用Parallel.ForEach? 我应该何时使用PLINQ?
PS :正如你所看到的,新的并行function并不像看起来那么明显。
当使用AsParallel()时,你需要确保你的身体是线程安全的。 不幸的是,上面的代码将无法正常工作。 List<T>
不是线程安全的,所以添加AsParallel()
会导致竞争条件。
但是,如果将集合切换到使用System.Collections.Concurrent中的集合(如ConcurrentBag<T>
,上面的代码很可能会起作用。
我宁愿为每个别名使用另一个数据结构(如Set),然后使用Set union来合并它们。
像这样的东西
public string[] ExpandAliases(string[] countries){ // Alias definitions var apac = new HashSet<string> { "US", "FR", ...}; ... var aliases = new HashMap<string, Set<string>> { {"APAC": apac}, ... }; var expanded = new HashSet<string> foreach(var country in countries){ if(aliases.Contains(country) expanded.Union(aliases[country]); else{ expanded.Add(country); } return expanded.ToArray(); }
注意:代码应该被视为伪代码。
这对我来说似乎是一个固有的连续操作。 你所做的只是遍历一个string列表并将它们插入到另一个列表中。 并行库将会这样做,再加上一堆线程和同步 – 它可能会变慢。
此外,如果你不想重复,你应该使用HashSet<string>
。