一个只读的HashSet本质上是线程安全的吗?

13
如果我在 Lazy 初始化器中初始化一个 HashSet<>,然后从未更改其内容,那么该 HashSet<> 是固有的线程安全的吗?是否有需要锁定的读取操作?
类似于Java中对集合的问题here,基本上是肯定的,但有一些注意事项(在这种情况下不适用)。

1
如果确实不会改变且在构建时不被访问,那么是的,你会没问题。话虽如此,没有什么能阻止别人去更改它,因此它的“readonly”行为是无法强制执行的。可以尝试使用不可变集合。 - Adam Houldsworth
@AdamHouldsworth 很不幸,不可变集合的性能非常糟糕。http://ayende.com/blog/164739/immutable-collections-performance - Chris Marisic
@ChrisMarisic 这个性能问题非常有名。尽管如此,在许多情况下,性能问题可能不会显现出来。你可以通过在内部包装非不可变集合来实现自己的优化,这些集合经过多年的优化处理。 - Adam Houldsworth
@AdamHouldsworth,如何通过包装缓慢的东西使其更快?更不用说保持不变性了吧?在C#中保持不变性非常困难。然而,在Ayende的评论中提到的代码提供了有趣的构造来自己实现。 - Chris Marisic
@ChrisMarisic 我的意思是将类似于数组或字典的东西包装起来,以便自己创建一个 API,一旦构建就不允许更改。我并不是指包装现有的不可变类型。 - Adam Houldsworth
1个回答

13

是的,只要 HashSet 对象的构造是线程安全的,并且其内容不发生改变,访问它将始终是线程安全的。

如果使用LazyThreadSafetyMode.PublicationOnlyinitializeLazy,则可以确保Lazy的初始化是线程安全的。

当多个线程同时尝试初始化Lazy<T>实例时,所有线程都被允许运行初始化方法(如果没有初始化方法,则运行默认构造函数)。完成初始化的第一个线程设置Lazy<T>实例的值。该值将返回给任何同时运行初始化方法的其他线程,除非初始化方法在这些线程上引发异常。

一个小代码示例:

var l = new Lazy<HashSet<string>>( () => new HashSet<string>() { "a" }
                                 , LazyThreadSafetyMode.PublicationOnly
                                 );

2
虽然我认为在实践中这是真的,但我找不到非修改HashSet操作是线程安全的保证。实际上,HashSet文档表明了相反的情况: https://msdn.microsoft.com/en-us/library/bb359438(v=vs.110).aspx "任何实例成员都不能保证是线程安全的"。您有证据证明在这种情况下可以保证线程安全吗? - Pagefault

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接