【问题标题】:ReadOnlyCollection<T> Thread SafetyReadOnlyCollection<T> 线程安全
【发布时间】:2012-01-26 01:59:46
【问题描述】:

ReadOnlyCollection(of T) 的文档指出:

A ReadOnlyCollection(Of T) 可以同时支持多个读取器,只要不修改集合即可。即便如此,通过集合进行枚举本质上不是线程安全的过程。为了保证枚举过程中的线程安全,可以在整个枚举过程中锁定集合。要允许集合被多个线程访问以进行读写,您必须实现自己的同步。

我的问题是关于粗体部分:

  1. 为什么通过集合枚举本质上不是线程安全的
  2. 可能的影响是什么,以及
  3. 常用的解决方法有哪些?

【问题讨论】:

标签: .net multithreading concurrency thread-safety immutability


【解决方案1】:

C# 有一个非常好的集合模型,但是 ReadOnlyCollection 类是整个模型中最不幸的构思(或命名)的类之一。它应该更恰当地称为只读列表,而不是只读集合。

现在,回答您的问题,它只是在构造时提供的 IList 的只读装饰器。因此,构造 ReadOnlyCollection 的代码可能会修改原始列表,这将对多线程访问产生所有后果。

因此,如果集合是真正只读的,则枚举集合将是线程安全的;但由于它不是只读的,因此它不是线程安全的。鉴于您的声誉,我很确定您不会想知道为什么枚举非只读集合不是线程安全的。

至于您询问的解决方法,好吧,您可以使用锁定,也可以使用无锁(或尽可能少地锁定)原则并制作真正的只读副本列表。

编辑

几个月后我重新阅读了我的答案(感谢 asyncwait 的评论),我意识到我应该回答所有 OP 的问题,而不是根据他的声誉做出假设。 OP现在可能已经收到了他的答案,但为了未来的读者,我会这样做。

通过非真正只读集合进行枚举本质上不是线程安全的,原因与即使在单线程场景中您在枚举集合时也无法修改集合的原因相同。 (Java 中的 ConcurrentModificationException,C# 中的 InvalidOperationException。)在单线程场景中,您可以确保枚举代码不会尝试以任何方式更改集合,但在多线程场景中,一个线程可能会在枚举集合的同时另一个线程可能正在同时更改它。

【讨论】:

  • 注意:我在写答案之前检查了 ReadOnlyCollection 的 SSCLI (Rotor) 源代码,以确保它确实是一个装饰器(包装器)。
  • 我第二个。它只是一个包装器。如果您的集合在外部被其他类修改,那将毫无用处。该名称在线程世界中具有误导性。
  • @asyncwait 感谢您的评论,它提醒我应该对我的答案进行修改。
  • @Mike Nakis 虽然这些年过去了,从那以后我学到了很多东西,但我很感激你花时间详细说明你的答案。谢谢。
  • “C# 有一个非常好的集合模型”。好笑话,让我开心。尤其是当您查看整个集合类型层次结构时。
【解决方案2】:

这个MSDN article staes:“ReadOnlyCollection 泛型类的实例始终是只读的。只读集合只是一个带有防止修改集合的包装器的集合

所以我认为迭代不是线程安全的,因为它在内部使用普通的非线程安全对象集合。

这意味着如果集合发生某种变化,不同的线程可能会得到不同的值。使用lock 语句避免不同线程同时访问集合。

【讨论】:

    【解决方案3】:

    问题 1 和 2 已在其他帖子中得到解答。

    对于 3,请使用 System.Collection.Immutable.ImmutableList

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-26
      • 2011-08-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-18
      相关资源
      最近更新 更多