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