.NET 4.5有一个新的命名空间System.Collections.Immutable
此包提供了线程安全的集合,并保证永远不会更改其内容,也称为不可变集合。
我糊涂了。是不是ReadOnlyCollection类已经解决了线程安全问题?为什么要使用ImmutableList呢?
我知道还有一个IReadOnlyList interface。这并不能隐式解决线程安全问题,因为其他线程可能会通过另一个接口编辑该对象。
ReadOnlyCollection<T>
没有解决任何线程安全问题。它只是Ilist<T>
的包装。它不会公开成员来修改集合,但您始终可以使用基础集合引用对其进行修改。
如果修改了基础集合,则枚举ReadOnlyCollection<T>
是不安全的。如果你这样做,你将获得相同的InvalidOperationException
消息“集合被修改;枚举操作可能无法执行......”。
只要未修改集合,ReadOnlyCollection可以同时支持多个读取器。即便如此,通过集合枚举本质上不是一个线程安全的过程。为了在枚举期间保证线程安全,您可以在整个枚举期间锁定集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。
另一方面,ImmutableList
是不可变的,因此本质上是线程安全的。
ReadOnlyCollection
,顾名思义,只能阅读。
另一方面,您可以通过调用ImmutableList
/ Add
/ Remove
方法向Clear
添加/删除项目,例如,返回一个新的不可变列表。
在多线程方案中,请注意只读集合仍然不是线程安全的。
来自ReadOnlyCollection<T>
文档:
...如果对基础集合进行了更改,则只读集合会反映这些更改
由于集合(如List<T>
和其他集合)不是线程安全的,因此不是只读集合。
重要提示:在MSDN中,您将找不到明确解释的一些极端情况。一些看似只读取集合内容的操作实际上正在修改集合的内部结构。为什么没有指定? - 一个明显的原因是因为这是一个不反映API的实现细节。结果是,即使你不修改包装在List<T>
中的ReadOnlyCollection<T>
,并且只使用getter,崩溃仍然可能发生在多线程环境中!
底线是常见的集合,即使在包装成ReadOnlyCollection
时也无法在开箱即用的多线程环境中使用。
与ReadOnlyCollection
相反,不可变集合确保在获得对集合的引用后,任何内部结构都不会发生变化。请注意,这些结构仍然不是真正不可变的。相反,它们是可以冻结的。这意味着该结构将在内部更改一段时间,直到它被冻结并返回给调用者。除此之外,对不可变集合的所有其他调用只会在通过原始引用可访问的结构之外进行修改。
结论:只读集合不是线程安全的;不可变集合是线程安全的。