2011-12-22 71 views
9

爲ReadOnlyCollection(的T)的文檔指出:ReadOnlyCollection <T>線程安全

A ReadOnlyCollection(Of T)可以支持多個讀者同時,只要收集不被修改。儘管如此,通過集合枚舉本質上不是一個線程安全的程序。爲了確保枚舉期間的線程安全性,您可以在整個枚舉過程中鎖定集合。爲了讓集合可以被多個線程讀取和寫入,您必須實現自己的同步。

我的問題關於粗體部分:

  1. 爲什麼枚舉通過集合本質上不是線程安全的
  2. 什麼是可能產生的影響,並
  3. 什麼常用的解決方法?
+1

Oh boy。在.NET上的安全列表時間過長。請參閱以前的一些討論:http://stackoverflow.com/questions/550616/lock -free-stack-and-queue-in-c-sharp和/或here:http://stackoverflow.com/questions/66622/threadsafe-foreach-enumeration-of-lists – Radu094 2011-12-22 12:15:46

回答

7

C#有一個相當不錯的集合模型,但ReadOnlyCollection類是整個模型中最不幸構思的(或命名的)類之一。應該更恰當地稱爲只讀列表,而不是隻讀列表。

現在,爲了解決您的問題,它只是在構建時提供的IList的只讀裝飾器。因此,構造ReadOnlyCollection的代碼可能會修改原始列表,並具有多線程訪問的所有後果。

因此,如果集合是真正只讀的,那麼通過集合枚舉將是線程安全的;但由於它不是隻讀的,因此它不是線程安全的。鑑於您擁有的信譽數量,我相當肯定您並不想知道爲什麼通過非只讀集合進行枚舉不是線程安全的。

至於你問的解決方法,你可以使用鎖定,也可以使用無鎖定(或儘可能少的鎖定)原則,並創建一個真正的只讀副本的名單。

編輯

我重新閱讀我的答案多月後,(感謝asyncwait的評論),我意識到,我應該已經回答了所有的OP的問題未做基於他的名譽的假設。 OP現在可能已經收到了他的答案,但我會爲未來的讀者而做。

枚舉通過一個非真正只讀集合本質上不是線程安全的,原因相同,即使在單線程場景中,枚舉集合時也不能修改集合。 (Java中爲ConcurrentModificationException,C#中爲InvalidOperationException)。在單線程場景中,可以確保枚舉代碼不會嘗試以任何方式更改集合,但在多線程場景中,一個線程可能正在枚舉集合另一個線程可能會同時更改它。

+0

注意:我檢查了SSCLI(轉子)在編寫我的答案之前,先閱讀ReadOnlyCollection的源代碼,以確保它確實是一個裝飾器(包裝器)。 – 2011-12-22 12:37:56

+0

我秒。它只是一個包裝。如果你的集合在其他類的修改之外被修改,那就沒用了。這個名字誤導了線程世界。 – asyncwait 2013-06-13 16:46:40

+0

@asyncwait感謝您的評論,它提醒我應該對我的回答添加修正。 – 2013-06-14 19:35:04

3

This MSDN article staes:「ReadOnlyCollection泛型類的一個實例始終是隻讀的。是隻讀的集合是簡單地用一個包裝的集合,阻止修改集合

所以我相信迭代不是線程安全的,因爲它在內部使用對象的正常的非線程安全的集合。

這樣做的影響是,如果集合發生變化,不同的線程可能會得到不同的值。使用lock語句可以避免同時從不同線程同時訪問集合。