【问题标题】:Using 'AsParallel()' / 'Parallel.ForEach()' guidelines?使用“AsParallel()”/“Parallel.ForEach()”指南?
【发布时间】:2010-09-23 17:15:54
【问题描述】:

寻求关于利用 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();
}

【问题讨论】:

  • 实际上,没有。我只是想以此作为练习来了解 .NET 中的新并行化。 :)

标签: c# .net multithreading parallel-processing


【解决方案1】:

几点。

只写countriesAndAliases.AsParallel() 是没有用的。 AsParallel() 在并行执行之后成为 Linq 查询的一部分。部分是空的,所以根本没有用。

通常您应该将foreach 替换为Parallel.ForEach()。但要注意不是线程安全的代码!你拥有了它。您不能只将其包装到 foreach 中,因为 List&lt;T&gt;.Add 本身不是线程安全的。

所以你应该这样做(对不起,我没有测试,但它编译):

        return countriesAndAliases
            .AsParallel()
            .SelectMany(s => 
                IsCountryNotAlias(s)
                    ? Enumerable.Repeat(s,1)
                    : AliasCountryLists[s]
                ).Distinct();

编辑

您必须确定另外两件事:

  1. IsCountryNotAlias 必须是线程安全的。如果是pure function就更好了。
  2. 同时没有人会修改AliasCountryLists,因为字典不是线程安全的。或者使用ConcurrentDictionary 来确定。

对您有帮助的有用链接:

Patterns for Parallel Programming: Understanding and Applying Parallel Patterns with the .NET Framework 4

Parallel Programming in .NET 4 Coding Guidelines

When Should I Use Parallel.ForEach? When Should I Use PLINQ?

PS:正如您所见,新的并行功能并不像看起来(和感觉)那么明显。

【讨论】:

    【解决方案2】:

    使用 AsParallel() 时,您需要确保您的主体是线程安全的。不幸的是,上面的代码不起作用。 List&lt;T&gt; 不是线程安全的,因此您添加 AsParallel() 将导致竞争条件。

    但是,如果您将集合切换为使用 System.Collections.Concurrent 中的集合,例如 ConcurrentBag&lt;T&gt;,则上述代码很可能会起作用。

    【讨论】:

    • "addition of AsParallel()" 因为它在上面的代码中完成不会导致竞争条件,因为在 c# 的 foreach 中它将被顺序处理。
    • @Andrey:是的 - 他必须使用 ParallelQuery 上的扩展方法(或 Parallel.ForEach 代替)才能产生任何效果。
    • @ReedCopsey , List 我知道不是线程安全的,但我已经知道 plinq 将工作分成块,然后收集结果(不像 parallel.foreach - 我必须这样做我自己的工作) - 那代码是线程安全的吗? i.imgur.com/mcEwWg2.png,我的意思是——我总是会得到 10 个不同的数字! ,那么它是否是线程安全的?
    【解决方案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();
    }
    

    注意:代码应视为伪代码。

    【讨论】:

    • 嗯,问题是关于 PLINQ 和 Parallel
    • 这也是关于加速,有时正确使用数据结构就足够了。
    【解决方案4】:

    对我来说,这似乎是一个固有的串行操作。您所做的只是遍历一个字符串列表并将它们插入另一个列表。并行化库会这样做,加上一堆线程和同步 - 它可能最终会变慢。

    另外,如果您不想重复,您应该使用HashSet&lt;string&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多