0

我写了一些代码来尝试描述我的担忧:

static void Main(string[] args)
{
    IEnumerable<decimal> marks = GetClassMarks();
    IEnumerable<Person> students = GetStudents();

    students.AsParallel().ForAll(p => GenerateClassReport(p, marks));

    Console.ReadKey();
}

GetClassMarks 使用来自我奇怪的数据源的收益返回。假设 GenerateClassReport 基本上是用 mark.Sum()/marks.Count() 来获得班级平均值。

据我了解,students.AsParallel().ForAll 是一个并行的 foreach。

我担心的是 GetClassMarks 方法内部会发生什么。

  • 它会被枚举一次还是多次?
  • 枚举将以什么顺序发生?
  • 我是否需要在标记上执行 .ToList() 以确保它只被击中一次?
4

3 回答 3

4

它会被枚举一次还是多次?

假设GenerateClassReport()枚举marks一次,thenmarks中的每个元素都会枚举一次students

枚举将以什么顺序发生?

每个线程将按其默认顺序枚举集合,但多个线程将同时这样做。并发枚举顺序通常是不可预测的。此外,您应该注意线程数是有限且可变的,因此很可能并非所有枚举都会同时发生。

我是否需要在标记上执行 .ToList() 以确保它只被击中一次?

如果GetClassMarks()是一个迭代器(即它使用该yield构造),那么它的执行将被推迟,并且每次marks枚举时都会调用一次(即对于 中的每个元素调用一次students)。如果你使用IEnumerable<decimal> marks = GetClassMarks().ToList()or 如果GetClassMarks()内部返回一个具体的列表或数组, thenGetClassMarks()将立即执行,结果将在每个并行线程中存储和枚举,而无需GetClassMarks()再次调用。

于 2010-07-05T14:47:42.757 回答
1
  1. IfGetClassMarks是一个迭代器——也就是说,如果它在yield内部使用——那么它实际上是一个查询,每当你调用时都会重新执行marks.Sum()marks.Count()等等。

  2. 在并行查询中预测执行顺序几乎是不可能的。

  3. 是的。以下将确保GetClassMarks只执行一次。marks.Sum()对等的后续调用marks.Count()将使用具体列表,而不是重新执行GetClassMarks查询。

    List<decimal> marks = GetClassMarks().ToList();
    

请注意,第 1点和第 3点适用,无论您是否使用AsParallel. 在任何一种情况下,GetClassMarks查询都将执行完全相同的次数(假设其余代码,除了并行方面,都是相同的)。

于 2010-07-02T12:38:43.443 回答
0

它会被枚举一次还是多次?

就一次。

枚举将以什么顺序发生?

迭代器(函数 using yield)确定顺序。

我是否需要在标记上执行 .ToList() 以确保它只被击中一次?

不。

AsParallel只遍历其输入一次,将输入划分为块,然后分派给工作线程。

于 2010-07-02T12:17:39.857 回答