使用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(); 

编辑

你必须确定另外两件事情:

  1. IsCountryNotAlias必须是线程安全的。 如果它是纯粹的function会更好。
  2. 同时,没有人会修改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>