IEnumerable yield return与.AsParallel()相结合

时间:2010-07-02 12:12:59

标签: .net-4.0 ienumerable parallel-processing plinq yield-return

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

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

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

    Console.ReadKey();
}

GetClassMarks从我奇怪的数据源中使用yield return。假设GenerateClassReport基本上使用marks.Sum()/ marks.Count()来获得类的平均值。

根据我的理解,Students.AsParallel()。ForAll是一个平行的foreach。

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

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

3 个答案:

答案 0 :(得分:4)

  

是否会被列举一次或多次?

假设GenerateClassReport()枚举marks一次,marks将为students中的每个元素枚举一次。{/ p>

  

枚举将以什么顺序发生?

每个线程将以默认顺序枚举集合,但是多个线程将同时执行此操作。并发枚举顺序通常是不可预测的。此外,您应该注意线程的数量是有限的和可变的,因此很可能并非所有枚举都会同时发生。

  

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

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

答案 1 :(得分:1)

  1. 如果GetClassMarks是迭代器 - 也就是说,如果它在内部使用yield - 那么它实际上是一个只要你调用{{1}就会重新执行的查询},marks.Sum()等。

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

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

    GetClassMarks
  4. 请注意,无论您使用List<decimal> marks = GetClassMarks().ToList(); ,点 1 3 都适用。在任何一种情况下,AsParallel查询都将执行完全相同的次数(假设除了并行方面之外的其余代码是相同的)。

答案 2 :(得分:0)

  

是否会被列举一次或多次?

只有一次。

  

枚举将以什么顺序发生?

迭代器(使用yield的函数)确定顺序。

  

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

没有

AsParallel只迭代其输入一次,将输入分区为分配给工作线程的块。