寻找有关利用AsParallel()
或Parallel.ForEach()
加快提升速度的建议。
请参阅下面的方法(本例中简化/标准化)。
它需要一个像“US,FR,APAC”这样的列表,其中“APAC”是50个其他“US,FR,JP,IT,GB”等的别名。该方法应采用“US,FR,APAC”,并将其转换为“US”,“FR”列表以及“APAC”中的所有国家/地区。
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();
}
答案 0 :(得分:66)
有几点。
仅仅写countriesAndAliases.AsParallel()
是没用的。 AsParallel()
成为Linq查询的一部分,该查询是在并行执行后生成的。部分是空的,所以根本没用。
通常您应该使用foreach
重新Parallel.ForEach()
。但要注意不是线程安全的代码!你拥有了它。您不能将其包装到foreach
中,因为List<T>.Add
本身不是线程安全的。
所以你应该这样做(对不起,我没有测试,但它编译):
return countriesAndAliases
.AsParallel()
.SelectMany(s =>
IsCountryNotAlias(s)
? Enumerable.Repeat(s,1)
: AliasCountryLists[s]
).Distinct();
修改强>:
你必须确定另外两件事:
IsCountryNotAlias
必须是线程安全的。如果它是pure function,那就更好了。AliasCountryLists
,因为字典不是线程安全的。或者使用ConcurrentDictionary来确定。有用的链接可以帮助您:
Parallel Programming in .NET 4 Coding Guidelines
When Should I Use Parallel.ForEach? When Should I Use PLINQ?
PS :正如您所看到的那样,新的并行功能并不像它们看起来那样明显(和感觉)。
答案 1 :(得分:13)
使用AsParallel()时,您需要确保您的身体是线程安全的。不幸的是,上面的代码不起作用。 List<T>
不是线程安全的,因此添加AsParallel()
会导致竞争条件。
但是,如果您将集合切换为使用System.Collections.Concurrent中的集合,例如ConcurrentBag<T>
,则上述代码很可能会有效。
答案 2 :(得分:3)
我更喜欢为每个别名使用另一个数据结构,比如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();
}
注意:代码应被视为伪代码。
答案 3 :(得分:0)
这对我来说似乎是一种固有的连续操作。您所做的只是循环遍历字符串列表并将它们插入另一个列表中。并行化库将会这样做,加上一堆线程和同步 - 它可能会变慢。
此外,如果您不想要重复,则应该使用HashSet<string>
。