2012-06-19 59 views
19

我不知道Linq擴展方法是原子嗎?或者我需要lock任何IEnumerable對象在任何類型的迭代之前在線程中使用?IEnumerable Linq方法是否線程安全?

是否聲明變量爲volatile對此有任何影響?

綜上所述,以下哪項最好,線程安全,操作?

1-無需任何鎖:

IEnumerable<T> _objs = //... 
var foo = _objs.FirstOrDefault(t => // some condition 

-2-包括鎖語句:

IEnumerable<T> _objs = //... 
lock(_objs) 
{ 
    var foo = _objs.FirstOrDefault(t => // some condition 
} 

3-聲明變量作爲揮發性:

volatile IEnumerable<T> _objs = //... 
var foo = _objs.FirstOrDefault(t => // some condition 
+0

他們不是線程安全的。見http://stackoverflow.com/questions/9995266/how-to-create-a-thread-safe-generic-list – stuartd

回答

20

接口IEnumerable<T>不是線程安全的。請參閱http://msdn.microsoft.com/en-us/library/s793z9y2.aspx上的文檔,其中聲明:

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

該枚舉器不具有對該集合的獨佔訪問權限;因此,通過集合枚舉本質上不是一個線程安全的過程。爲了確保枚舉期間的線程安全性,您可以在整個枚舉過程中鎖定集合。爲了讓集合可以被多個線程讀取和寫入,您必須實現自己的同步。

Linq不改變任何這一點。

鎖定顯然可用於同步訪問對象。您必須在您訪問它的任何地方鎖定對象,而不僅僅是在迭代它時。

將該集合聲明爲volatile將不會產生積極影響。它只會在讀取之前以及寫入對集合的引用之後導致內存屏障。它不會同步收集讀取或寫入。

7

總之,它們不是像上面提到的那樣是線程安全的。

但是,這並不意味着你必須在「每一次迭代」之前鎖定。

您需要將更改集合(添加,修改或移除元素)的所有操作與(添加,修改,移除元素或讀取元素)的其他操作同步。

如果您只同時對集合執行讀取操作,則不需要鎖定。 (所以運行LINQ命令如Average,Contains,ElementAtOrDefault都可以)

如果集合中的元素是機器字長度,例如大多數32位計算機上的Int,則更改該元素的值已經以原子方式執行。在這種情況下,不要在沒有鎖定的情況下從集合中添加或刪除元素,但如果您可以在設計中處理某些非確定性,則修改值可能沒問題。

最後,您可以考慮對集合中的單個元素或部分進行精細鎖定,而不是鎖定整個集合。