我写了一些代码来试图描述我的担忧:
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方法中会发生什么。
答案 0 :(得分:4)
是否会被列举一次或多次?
假设GenerateClassReport()
枚举marks
一次,marks
将为students
中的每个元素枚举一次。{/ p>
枚举将以什么顺序发生?
每个线程将以默认顺序枚举集合,但是多个线程将同时执行此操作。并发枚举顺序通常是不可预测的。此外,您应该注意线程的数量是有限的和可变的,因此很可能并非所有枚举都会同时发生。
我是否需要对标记执行.ToList()以确保它只被击中一次?
如果GetClassMarks()
是一个迭代器(即它使用yield
构造),那么它的执行将被推迟,并且每次枚举marks
时它将被调用一次(即一次对于students
中的每个元素。如果您使用IEnumerable<decimal> marks = GetClassMarks().ToList()
或GetClassMarks()
在内部返回具体列表或数组,则GetClassMarks()
将立即执行,结果将在每个并行线程中存储和枚举,而不调用{ {1}}再次。
答案 1 :(得分:1)
如果GetClassMarks
是迭代器 - 也就是说,如果它在内部使用yield
- 那么它实际上是一个只要你调用{{1}就会重新执行的查询},marks.Sum()
等。
几乎不可能预测并行查询中的执行顺序。
是。以下内容将确保marks.Count()
仅执行一次。对GetClassMarks
,marks.Sum()
等的后续调用将使用具体列表,而不是重新执行marks.Count()
查询。
GetClassMarks
请注意,无论您使用List<decimal> marks = GetClassMarks().ToList();
,点 1 和 3 都适用。在任何一种情况下,AsParallel
查询都将执行完全相同的次数(假设除了并行方面之外的其余代码是相同的)。
答案 2 :(得分:0)
是否会被列举一次或多次?
只有一次。
枚举将以什么顺序发生?
迭代器(使用yield
的函数)确定顺序。
我是否需要对标记执行.ToList()以确保它只被击中一次?
没有
AsParallel
只迭代其输入一次,将输入分区为分配给工作线程的块。