2010-05-12 66 views
12

我打算在靜態構造函數中創建一次列表,然後讓該類的多個實例同時讀取(並枚舉)它,而不進行任何鎖定。C#列表的線程安全<T>讀者

在這篇文章中 http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx MS介紹線程安全的問題如下:

公共靜態(在Visual Basic中的Shared)這種類型的 成員都是線程安全的。 任何實例成員不是 保證是線程安全的。

只要 集合未被修改,列表可同時支持多個閱讀器 。枚舉集合 本質上不是線程安全的 過程。在枚舉的 與一個或多個 寫入訪問競爭的罕見情況下,確保 線程安全的唯一方法是在整個枚舉中鎖定 集合。要允許 由多個線程訪問 讀取和寫入,您必須執行您自己的同步 。

The 「通過集合枚舉本質上不是線程安全的過程。」 聲明是我擔心的。

這是否意味着它是線程安全的讀者唯一的情況下,但只要你不使用枚舉?

或者對我的情況安全嗎?


感謝您的回答。 爲什麼我需要使用AsReadOnly,如果它可以使用或不使用它?

回答

1

這是否意味着它是線程安全的 讀者僅情形,但只要 只要你不使用枚舉?或者是 它對我的情況安全嗎?

完全取決於您何時寫入集合。這與閱讀(枚舉)沒有某種鎖定方案不相符。

所以,如果你填充一次,然後只遍歷它,你是安全的。但是當一個線程更改列表(添加或刪除項目)時,您將需要例如ReaderWriterLockSlim。

當您更改存儲的項目的狀態時,線程安全與該項目(不是列表)。

8

他們的意思是,如果你列舉的集合,而在不同的線程(或自己的線程)改變它,你就會有問題。

只要你根本不改變集合,只要你不跨線程共享IEnumerator,你就不會有任何問題。

+0

通過'不要共享IEnumerators跨線程'你的意思是'不從多個線程訪問**相同的枚舉器實例**? – 2014-07-08 07:25:32

+1

@EugeneBeresovksy:的確如此。 – SLaks 2014-07-08 13:57:35

3

是的,列表是安全的讀者只有的情況下,如果列表永遠不會被修改,那麼它會沒事的。

如果事實上是在施工後列表不會被修改,那麼你應該使用更合適的接口,如ReadOnlyCollection。如果它被存儲在一個公共靜態變量中,如你所說,你應該使用該接口。

private static List<T> shared_list; 

private static ReadOnlyCollection<T> _data; 
public static IEnumerable<T> Data 
{ 
    get 
    { 
     return _data ?? (_data = shared_list.AsReadOnly()); 
    } 
} 

==== ====編輯

這個版本緩存更快的未來查找時間的ReadOnlyCollection參考。

請注意,如果兩個線程在空值時嘗試同時獲取引用,但是因爲所有內容都是隻讀的,這並不重要,所以我們將只在_data變量上發生數據競爭創建一個額外的ReadOnlyCollection對象,與同步相比,它便宜。

+0

請注意,您仍然不能更改原始列表。另外,每次你獲得財產時都不要調用'AsReadOnly'。 – SLaks 2010-05-12 22:17:23

+0

AsReadOnly是根據MSDN文檔的O(1)操作。我相信它只是簡單地委託給底層集合的ReadOnlyCollection包裝。所以AsReadOnly應該很快,但它確實會導致分配,所以我們可能會共享該內存。虐待編輯來反映。 – luke 2010-05-12 22:58:11

+0

在.NET 4下,您可以使用通用的Lazy類進行初始化。請參閱http://msdn.microsoft.com/en-us/library/dd642329.aspx – TrueWill 2010-12-17 02:21:45