【问题标题】:Are the two approaches behave the same?这两种方法的行为是否相同?
【发布时间】:2018-06-09 06:08:49
【问题描述】:

以下两个代码并行处理列表中的项目,并行度阈值为 5。它们实际上是否相同?

Parallel.ForEach 与 MaxDegreeOfParallelism

public async Task Run()
{
    var list = // List<....>
    Parallel.ForEach(list, 
        new ParallelOptions { MaxDegreeOfParallelism = 5}, d => {
            ....
            process(d).RunSynchronously();
            ....
    });
}

使用Partitioner.Create(...).GetPartitions(...)的扩展方法

public async Task Run()
{
    var list = // List<....>
    await list.ForEachAsync(list.Count() / 5 + 1, async d =>
    {
        ....
        await process(d);
        ....
    });
}

public static class Extension
{
    public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
    {
        return Task.WhenAll(
            from partition in Partitioner.Create(source).GetPartitions(dop)
            select Task.Run(async delegate {
                using (partition)
                    while (partition.MoveNext())
                        await body(partition.Current);
            }));
    }
}

【问题讨论】:

  • 第一个向Parallel.Foreach 发送一个异步委托,这会导致一个未等待的异步无效。 Parallel.Foreach 不适用于异步操作,而仅适用于 CPU 绑定代码。
  • 我已经更新了问题并为第一个问题同步调用了异步方法。
  • 五个反对者,为什么?

标签: c# task-parallel-library


【解决方案1】:

Parallel.Foreach 使用默认分区程序。如果您有一个小工作要执行,那么这可能是开销,因为每个分区都将被计算并且每个分区都将是每次迭代都会调用的委托。

可以使用分区来修复此开销。它使您能够为委托主体提供顺序循环,以便每个分区仅调用一次委托,而不是每次迭代调用一次。您也可以使用分区器来控制分区。

 Parallel.ForEach(Partitioner.Create(0L, SUMTOP), (range) =>
            {
                long local = 0;
                for (long i = range.Item1; i < range.Item2; i++) local += i;
                Interlocked.Add(ref sum, local);
            });

第二个实现做类似的工作,但异步,你有更多的分区命令。它将处理小型工作的开销。 希望这个解释能帮助你做出选择。

【讨论】:

  • 第一个实现中的MaxDegreeOfParallelism 会控制分区吗?不确定您答案的第一段,该列表可能有几个到数百个项目。
  • Small job 指的是每次迭代中会发生的工作负载。你有多少次迭代并不重要。如果工作负载很小,则顺序可能运行得更快,然后并行。
猜你喜欢
  • 1970-01-01
  • 2013-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多