0

我已經寫了一些代碼,試圖描述我的關注:IEnumerable的收益率與回報相結合.AsParallel()

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

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

    Console.ReadKey(); 
} 

GetClassMarks使用從我奇怪的數據源中它的產量回報。假設GenerateClassReport基本上用marks.Sum()/ marks.Count()來獲得班級平均值。

從我的理解,students.AsParallel()。ForAll是一個並行的foreach。

我的擔心是GetClassMarks方法中會發生什麼。

  • 它會被枚舉一次或多次?
  • 枚舉將在什麼順序發生?
  • 我是否需要對標記執行.ToList()以確保它僅被命中一次?

回答

4

它會被枚舉一次或多次?

假設GenerateClassReport()枚舉marks一次,然後marks將一次在students每個元素計數。

枚舉將在什麼順序發生?

每個線程都會按默認順序枚舉集合,但有幾個線程會同時這樣做。併發枚舉順序通常是不可預知的。此外,你應該注意線程的數量是有限和可變的,所以很可能不是所有的枚舉都會同時發生。

我是否需要對標記執行.ToList()以確保它僅被命中一次?

如果GetClassMarks()是一個迭代(即,它使用yield構建體),那麼它的執行將被推遲,這將一次每次marks被枚舉爲(在students即一次爲每個元件)被調用。如果使用IEnumerable<decimal> marks = GetClassMarks().ToList()GetClassMarks()內部返回具體列表或數組,則將立即執行GetClassMarks(),並且結果將被存儲並枚舉到每個並行線程中,而不會再次調用GetClassMarks()

0

它會被枚舉一次或多次?

只要一次。

枚舉將在什麼順序發生?

迭代器(使用yield的函數)確定順序。

我是否需要對標記執行.ToList()以確保它僅被命中一次?

AsParallel只有通過它的輸入一次迭代,將所述輸入到其中被分派到工作線程塊。

1
  1. 如果GetClassMarks是一個迭代器 - 也就是說,如果它使用yield內部 - 那麼它實際上是將每當調用marks.Sum()marks.Count()

  2. 它被重新執行查詢幾乎不可能預測並行查詢中的執行順序。

  3. 是的。以下內容將確保GetClassMarks僅執行一次。隨後調用marks.Sum(),marks.Count()等將使用具體列表而不是重新執行GetClassMarks查詢。

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

注意你是否正在使用AsParallel指向和適用。在這兩種情況下,GetClassMarks查詢都將執行完全相同的次數(假設其餘代碼除並行方面外都相同)。