Parallel.ForEach 保持集合顺序吗?

6
在使用Parallel.ForEach()时,有没有一种方法可以保证顺序?我正在遍历的集合需要保持其顺序,但我正在寻找一些性能改进。

1
“保证顺序”是什么意思?举个例子,假设你有5个线程,第3个项目在第4个项目之前完成,那么线程3将从第6个项目开始,这样继续下去,你无法确定顺序。你是想解决这个问题吗? - James Black
5个回答

10
为了保持顺序,在传递列表到foreach循环之前,您必须尝试对列表进行排序,因为默认情况下Parallel.Foreach将列表视为无序。
示例:
Parallel.ForEach(
list.AsParallel().AsOrdered(), 
(listItems) => {<operations that you need to do>});

10
你有一个类似于这样的语句吗?(基于你上面的评论)
Parallel.Foreach(myData, ..., (d) =>
{
  StringBuilder sb = new StringBuilder();
  sb.Append(d);
  // WriteLine sb?
});

这种方法存在一些问题:
  1. 无论是使用 Parallel.For 还是 Parallel.ForEach,都不能保证您对 myData 内容的访问顺序。
  2. 如果您是在 Console 上的方法或共享的 StringBuilder 中输出结果或构建完整字符串,则可能会阻塞共享资源,从而使并行循环的某些部分变为串行化。
没有看到您代码的具体示例,很难再多说。根据您所做的工作,您可能能够使用 PLINQ 中的顺序保存 AsOrdered() 来达到目的。
请参见 MSDN 资源有序 PLINQ ForAll
这将允许您返回一个基于输入顺序的有序结果集,但不能保证实际处理的顺序。但是,如果并行查询在主体中调用时被阻塞,您不太可能获得良好的性能。

1

将此作为扩展方法完成

 public static IEnumerable<T1> OrderedParallel<T, T1>(this IEnumerable<T> list, Func<T, T1> action)
    {
        var unorderedResult = new ConcurrentBag<(long, T1)>();
        Parallel.ForEach(list, (o, state, i) =>
        {
            unorderedResult.Add((i, action.Invoke(o)));
        });
        var ordered = unorderedResult.OrderBy(o => o.Item1);
        return ordered.Select(o => o.Item2);
    }

use like:

var result = input.OrderedParallel(o => YourFunction(o));

希望这能为您节省一些时间。

怎么使用?当我在List<int>集合上使用OrderedParallel时,我收到错误:"方法“IEnumerable<T1> OrderedParallel<T,T1>(this IEnumerable<T>, Func<T,T1>)”的类型参数无法从使用中推断出来。请尝试显式地指定类型参数。" - Tomas

-1

对于任何寻找简单解决方案的人,我已经发布了两个扩展方法(一个使用PLINQ,另一个使用Parallel.ForEach),作为回答以下问题的一部分:

有序PLINQ ForAll


-3

不,ForEach 只用于顺序无关紧要的情况;请尝试使用 Parallel.For


Parallel.For会保持我的集合的顺序吗? - Brian David Berman
我猜我应该问一下你想要维护什么样的顺序——是像James Black所说的那样,你想要处理你的IEnumerable中的项目吗?还是你想要确保进去的东西以相同的顺序出来?如果是后者,那么使用for或foreach都可以。如果是前者,那么你可能不想使用并行函数,因为按顺序操作项目会抵消并行操作的收益。 - CodeMonkey1313
我在我的foreach循环中使用StringBuilder.WriteLine(),创建了一行已经排序好的数据。 - Brian David Berman
根据您对数据的排序方式,您可能可以将排序功能放入并行循环中,但我建议仅使用内置的.NET选项。最终,我认为并行循环不是您的正确解决方案,因为您需要顺序操作。 - CodeMonkey1313
这是不正确的。Parallel.For将不会维护输入数据相对于任何输出的顺序,就像Parallel.ForEach一样。请参见下面的答案。我必须给它点踩因为它是错误的。 - Ade Miller
请将Parallel.For编辑为Parallel.ForAll。+1给#Ade-Miller! - TamusJRoyce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接