2014-02-26 215 views
2

我面對這個代碼的問題:線程安全OfType

enumerable.OfType<Foo>().Any(x => x.Footastic == true); 

心不是線程安全的,並拋出一個枚舉已經改變例外。

有沒有解決這個問題的好方法?

已經試過以下,但它沒有總是工作(似乎不經常觸發此)如果您收到一個例外,雖然它枚舉底層集合改變了,給

public class Foo 
{ 
    public void DoSomeMagicWithCollection(IEnumerable enumerable) 
    { 
     lock (enumerable) 
     { 
      enumerable.OfType<Foo>().Any(x => x.Footastic == true); 
     } 
    } 
} 
+3

在這種情況下'enumerable'是什麼?如果你想迭代一個基本上不安全的集合來迭代,那麼無論你做什麼,你都會遇到問題。請注意'OfType'不是這裏的罪魁禍首。 –

+0

僅當某些代碼*更改了可枚舉值時纔會出現此錯誤。你發佈的代碼不會出現,所以它應該是線程安全的。你究竟在哪裏得到例外?什麼是調用堆棧?你如何創建IEnumerable?你有其他代碼修改另一個線程的枚舉嗎? –

+0

無論你對可枚舉做什麼,你都需要一個鎖對象。別的地方你正在修改枚舉而不使用鎖。 – Kevin

回答

6

,該代碼清楚不會改變集合本身,這意味着另一個線程正在嘗試迭代它時突變集合。

這個問題沒有可能的解決辦法,只是沒有做到這一點。發生什麼情況是List(或任何正在收集的類型)的枚舉數正在拋出異常並阻止進一步的枚舉,因爲它可以看到列表在枚舉期間被修改。 OfType的調查員沒有辦法將包裝爲可能從中恢復。底層的枚舉器拒絕給他們從列表中的數據。他們對此無能爲力。

您需要使用某種同步機制來防止另一個線程對集合進行變異,而這個線程正在枚舉這個集合。您的lock不會阻止另一個線程使用該集合,它只會阻止鎖定在同一實例上的任何代碼運行。你需要有任何代碼可能會改變列表鎖定在同一個對象正確同步它們。

另一種可能性是使用固有設計的集合,以便同時從多個線程訪問。在System.Collections.Concurrent命名空間中有幾個這樣的集合。他們可能會或可能不適合您的需求。他們將獨立處理同步對其數據的訪問(到某一點),而無需在訪問它們時明確鎖定。

+0

System.Collections.Concurrent命名空間中的哪個類是否會推薦用於替換當前的類公有類ObservableSortedList的類定義:IList IList,IEnumerable ,INotifyPropertyChanged,INotifyCollectionChanged'是否帶有? –

+0

@RandRandom考慮到我不知道你對集合做了什麼,我不可能說,這就是爲什麼我沒有命名一個類,我只是命名了命名空間。很明顯,沒有一個集合實現所有這些接口。 – Servy