首页 > 解决方案 > 这两种方法的行为是否相同?

问题描述

以下两个代码并行处理列表中的项目,并行度阈值为 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);
            }));
    }
}

标签: c#task-parallel-library

解决方案


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);
            });

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


推荐阅读