【问题标题】:Is it safe for multiple threads to read from a Lookup<TKey, TElement>?多个线程从 Lookup<TKey, TElement> 中读取是否安全?
【发布时间】:2018-09-15 02:31:35
【问题描述】:

多个线程从Lookup&lt;TKey, TElement&gt; 中读取是否安全?

Lookup&lt;TKey, TElement&gt; 是不可变的,但是 MSDN 声明:

此类型的任何公共静态(在 Visual Basic 中为共享)成员都是 线程安全。不保证任何实例成员都是线程 安全。

虽然我不敢想象,但我想知道抽出 MSDN 文档的机器是否 可能不正确。

【问题讨论】:

  • 对不起,我忘了这里不允许玩。
  • 你能想象一个多线程可能在读取不可变数据结构时出现问题的情况吗?
  • @ChaosPandion:当然。某些东西可能在外部是不可变的,但在内部会改变状态(例如,用于缓存) - 在这种情况下,通过公共 API 读取的两个线程可能可能会破坏内部状态。
  • @RonnieOverby:我不知道,不。我强烈怀疑它是线程安全的,但我不想保证。还有其他原因导致读取可能不是线程安全的 - 例如内存模型会从某些线程中提供“陈旧”读取。
  • @Jon Skeet - 如果你问我,这听起来像是一段伪劣的代码。 :)

标签: c# .net multithreading data-structures lookup


【解决方案1】:

因为我不想冒险在 1 年后可能不得不调试一个晦涩难懂的多线程相关错误,所以我假设在没有手动同步的情况下使用这个类是不安全的。

【讨论】:

    【解决方案2】:

    只要不写,只做读是线程安全的。这在任何情况下都是有效的。

    您的问题在某种意义上与线程安全的概念正交。写入与写入或读取的组合不是线程安全的,但不写入的多次读取是线程安全的。

    MSDN 关于实例成员不能保证是线程安全的说法只能在非线程安全场景的情况下有效,根据定义,这意味着写入操作。

    【讨论】:

    • "只要不写,只做读是线程安全的。这在任何情况下都是有效的。" - 这不是真的。考虑一下内存模型——例如,一个线程可能在它能够“看到”所有条目的数据之前“看到”查找。这取决于内存屏障是如何设置的。
    • 当我从其中一个成员那里“读取”该类时,我不知道该类在内部做了什么。我相信我可以对文档中明确说明的类的并发读取感觉良好,“嘿,可以这样做!”
    • @Jon Skeet:是的,这是一个很好的观点。我想这取决于您对线程安全的定义,是否将可见性作为要求。
    • 我对线程安全的定义是:当一个操作在编程时考虑到并发访问场景,就可以说它是线程安全的。
    • @Tudor:如果你看到部分数据但不是全部,那对我来说听起来并不“安全”...
    【解决方案3】:

    这是您可能已经注意到的all 大部分课程的标准免责声明。有些方法可能是线程安全的,但“不能保证”。

    一般来说,如果没有写入者来收集,则使用多个线程从集合中读取是安全的。如果您需要同时更新集合 - 使用适当的同步或内置线程安全集合,例如 SynchronizedKeyedCollection

    【讨论】:

    • 并非所有课程都有此免责声明。查看Dictionary&lt;TK,TV&gt;BlockingCollection&lt;T&gt; 或任何Concurrentxxxx&lt;T&gt;。它们让您知道与这些对象的部分或全部交互对于多线程是安全的。
    • 谢谢。 “全部”替换为“大多数”。
    【解决方案4】:

    因为Lookup&lt;TKey,TElement&gt; 是不可变的,这意味着您将为所有成员获得相同的值。这并不意味着其中存储的项目不能被修改。所以集合确实不是线程安全的。一个完美的例子是大多数 linq 都是惰性求值的,创建枚举器可能涉及执行惰性代码。尝试在两个单独的线程中枚举可能会导致集合被实现两次,从而产生错误的结果。

    更新: 现在源代码在 https://referencesource.microsoft.com 上可用,可以确认在方法调用期间设置了内部状态,而不考虑多线程,这意味着您可能存在竞争条件,Lookup&lt;TKey,TElement&gt; 类实际上不是线程安全的。

    【讨论】:

    • ToLookup 不是惰性的,而是创建一个不可变的集合。它的项目不是线程安全的事实并不能证明集合不是线程安全的。否则concurrent namespace 中的所有集合也不是线程安全的。
    • @TimSchmelter 它在文档中明确指出 Lookup&lt;TKey,TElement&gt; 不是原始问题中提出的线程安全。我在解释原因。
    • 如果您查看文档,则不再提及
    • 在新文档中,如果您查看 Skip 之类的方法,您会发现它基本上说明了我的回答。
    • ToLookup 并不懒惰,所以我看不出它是如何相关的 这甚至不是关于方法 ToLookup 而是关于它的结果,集合中的项目可以修改并不重要.你不能在不创建新集合的情况下改变这个集合,因此集合本身是线程安全的,至少“大部分”是线程安全的(阅读 Jon Skeets cmets)。
    猜你喜欢
    • 2010-11-27
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    相关资源
    最近更新 更多