2014-03-28 87 views
3

是否可以並行化一個循環的長度增加的循環?添加到列表中的並行循環

List<int> list = new List<int>() { 0, 1 }; 

for (int i = 0; i < list.Count; i++) 
//Parallel.For(0, list.Count, (i) => 
{ 
    Console.WriteLine(list[i]); 
    if (i == 0) list.Add(2); 
}//); 

//foreach (int i in list) 
//Parallel.ForEach(list, (i) => 
//{ 
// Console.WriteLine(i); 
// if (i == 0) list.Add(2); 
//}//); 

Console.ReadLine(); 

在這個簡單的例子中,期望的輸出是:

0 
1 
2 

上面的代碼正確工作與「爲」串行,但失敗了串行「的foreach」由於集合被修改。對於兩個並行實現,代碼完成,但輸出缺少最終的'2'。

+0

您可以將代碼拆分爲線程安全的工作項目列表和一些可以多線程處理工作項的代碼。使用'下一個'工作項目和一些鎖定的指針,你應該沒問題。 – CodingBarfield

回答

3

爲每個循環更改a中的集合是無效的。基本上以任何方式修改列表使枚舉器無效。以下是來自IEnumerator的文檔的引用:

只要集合保持不變,枚舉數仍然有效。如果對集合進行更改(如添加,修改或刪除元素),則枚舉器將無法恢復無效,並且其行爲未定義。

欲瞭解更多信息,請看看this post。至於並行實現:

  • Parallel.ForEach - 這是受相同的IEnumerator問題,對於每一個標準是
  • Parallel.For - 這通過循環進數爲爲常數,而不是作爲參考。這意味着當計數發生變化時,它不會改變它將循環的次數。

更安全的模式是在調用並行實現之前添加,刪除和修改列表元素。然後線程可以處理這些元素。如果無法完成,請確定循環後您將擁有的元素數量,然後使用數組通過索引來存儲/處理這些元素。最後將任何非空值返回到列表中。這樣您就不必擔心線程安全問題(Insert會推動其他元素前進,導致索引無效)。下面應該工作:

// EX: might be initialized with a call to the database: "COUNT(id)" 
int expectedElements = 10; 
if (myList.Count < expectedElements) 
    for (var idx = myList.Count; idx <= expectedElements; idx++) myList.Add(null); 

var elements = myList.ToArray(); 
System.Threading.Tasks.Parallel.For(0, expectedElements, (idx) => 
{ 
    // "remove" the element 
    if (idx % 3 == 0) elements[idx] = null; 

    // "modify" the element 
    if (idx % 3 == 1) elements[idx] = DifferentElement(idx); 

    // "add" an element 
    if (idx % 3 == 2) elements[idx] = GetNewElement(idx); 
}); 

// clear current list, add new elements, remove null values 
myList.Clear(); 
myList.AddRange(elements); 
myList.RemoveAll(item => item == null); 

現在你可以「添加」,「刪除」和你想要的「修改」儘可能多的,結果是返回到列表中!

0
for (int i = 0; i < list.Count; i++) //list.Count will only checked at first call 
{ 
    Console.WriteLine(list[i]); 
    if (i == 0) list.Add(2); 
} 

聽起來像你list.Count會問了一句,然後將它保存在內存中,在你的情況,list.Count將是2和永動,所以你將打印列表[0],然後列表[1]。
您也可以通過鎖定感興趣:

線程A:

lock (list) { 
    foreach (Object obj in list) { 
     obj.doSomething(); 
     if(meet_condition) list2.add(obj) 
    } 
} 

凡列表2是一個靜態屬性。

線程B:

lock (list) { 
    list.Remove(Element); 
} 

只要線程鎖列表,其他線程等待,直到它被釋放到使用它。 不知道你試圖用它做什麼,很難幫助你。

+0

是的,它看起來像循環重新評估每次通過,所以它看到list.Count已增加。順便說一下,並行實現不會重新評估。有沒有解決的辦法? – user1765603

+0

如果您的目標是在控制檯中打印數字,您可以從列表中覆蓋添加方法,而不是簡單地添加到列表中,您可以添加一個 Console.WriteLine(value)然後list.Add(value); –

+0

這是我面對的問題的一個簡單例子。實際的算法要複雜得多。 – user1765603