2009-04-21 31 views
2

如何找出哪個函數或目標正在修改多線程應用程序的foreach循環中的項目?查找如何修改foreach集合

我不斷收到錯誤「Collection was modified; enumeration operation may not execute」。我不會在for循環中刪除或添加任何項目到通用列表中。 我想知道它是如何被修改的。 這樣做的最好方法是什麼?

感謝

+0

這個類,其中包含foreach循環是通用列表的一部分。 除此之外,該通用列表中的此類的每個實例都在單獨的線程中運行 – Josh 2009-04-21 13:23:52

回答

7

等一下 - 我想我們都錯過了這一點。

集合類不是線程安全的。如果集合正在被多線程訪問而沒有線程鎖定,那麼你需要修復這個問題。這是一個特別隱祕的問題,因爲它會正常工作[編輯 - 或拋出一個可預見的例外] 99.999%的時間,0.001%的時間,它會做一些完全不可預測,幾乎不可能重現的事情。

一個簡單的方法是在任何代碼訪問集合的每個地方使用lock {}語句。這可能有點矯枉過正,但這是最安全的計劃。對於迭代,你可以把一個鎖周圍的整個循環,或者如果你不想阻擋其他線程那麼久,只是鎖定它足夠長的時間,使快照:

object[] snap; 
lock (list) 
{ 
    snap = list.ToArray(); 
} 
foreach (object x in snap) ... 
+1

併發不需要解決此問題。循環內調用的方法可能會導致修改集合的代碼。 – Richard 2009-04-21 14:30:14

0

這樣做是爲了收集不暴露在許多類這有可能改變它的最好辦法。使它對一個特定的類是私有的,那麼只有該類的方法纔可能改變它。

否則,您需要從集合類創建派生類,並覆蓋所有可更新集合的方法。在所有覆蓋中設置斷點。

0

將全局集合的內容複製到更緊密的範圍變量以保護外部數據干預是否可行?

2

這不是一個直接的答案,只是一個建議,可能會幫助 - 在多線程的情況下收集可能迭代過程中得到修改,我一般使用的ToArray方法創建列表的迭代快照:

foreach (object x in list.ToArray()) ... 
1

將集合替換爲ObservableCollection,將事件附加到修改中,並在調試器(或捕獲堆棧跟蹤等)中打斷它們。

0

你應該能夠設置一個異常斷點,然後只需要通過每個線程看看哪一個修改了這個集合。無論如何,如果該集合在多個線程之間共享,則需要進行適當的同步。

0

您也可以嘗試更改集合的類名,以查看其全部引用的位置。