【问题标题】:Why are there no concurrent collections in C#?为什么 C# 中没有并发集合?
【发布时间】:2009-12-22 13:57:03
【问题描述】:

我想大致了解 C# 中集合背后的线程安全理论。

为什么没有 Java 中的并发集合? (java docs)。有些集合看起来是线程安全的,但我不清楚该位置是什么,例如:

  • 复合运算,
  • 使用迭代器的安全性,
  • 写操作

我不想重新发明轮子! (我不是多线程专家,我绝对不会低估这将是多么困难)。

我希望社区可以提供帮助。

【问题讨论】:

  • 很好的回应 - 我将把这个“未回答”留一小段时间,以保持它在雷达上。如果有人有任何进一步链接到关于此主题的 .Net 4.0 之前或之后的文章,请包括。谢谢大家。

标签: c# java multithreading concurrent-collections


【解决方案1】:

到目前为止,.NET 具有相对“低级”的并发支持 - 但 .NET 4.0 引入了 System.Collections.Concurrent 命名空间,其中包含各种安全且有用的集合。

就如何处理 .NET 4.0 之前的集合而言,Andrew 的回答当然是完全正确的 - 对于 大多数 用途,我只需在访问“正常”共享集合时适当地锁定即可。然而,并发集合使得使用生产者/消费者队列等变得容易。

【讨论】:

  • +1 感谢乔恩。我想这样 .net 不会让人们产生虚假的安全感。我将查看 .Net 4.0 的好东西。 – 安德鲁
【解决方案2】:

C# 提供了几种跨多个线程处理集合的方法。为了更好地了解这些技术,我建议您从 Collections and Synchronization (Thread Safety) 开始:

默认情况下,集合类是 通常不是线程安全的。多 读者可以阅读该集合 信心;然而,任何修改 到集合产生未定义 访问的所有线程的结果 收藏,包括读者 线程。

集合类可以做成线程 安全使用以下任何一种 方法:

  • 使用 Synchronized 方法创建一个线程安全的包装器,并且 以独占方式访问该集合 通过那个包装器。
  • 如果类没有同步方法,则从 类并实现一个 Synchronized 使用 SyncRoot 属性的方法。
  • 使用锁定机制,例如 C# 中的 lock 语句(SyncLock in Visual Basic),在 SyncRoot 上 访问时的属性 收藏。

【讨论】:

  • +1 太快了! - 这看起来有点像 Java 中的 Collections.synchronizedList。我会检查那个链接。谢谢
  • 另见此处:blogs.msdn.com/ericlippert/archive/2008/01/21/… Eric Lippert 在此深入讨论了不可变集合,特别是关于并发访问。
  • 感谢 Jeremy 没有来过这个博客(现已订阅),在不变性标签下有一些优秀的材料。
【解决方案3】:

正如 Jon Skeet 所提到的,现在 .NET 4 的 System.Collections.Concurrent 命名空间中有“线程安全”集合。

在以前的 .NET Framework 版本中不存在并发集合(至少我的猜测)的原因之一是很难保证线程安全,即使使用并发集合也是如此。

(这并不完全正确,因为某些集合提供了一种同步方法来从非线程安全集合返回线程安全集合,因此存在一些线程安全集合...)

例如假设一个人有一个线程安全的字典 - 如果一个人只想在 Key 不存在的情况下进行插入,那么首先会查询集合以查看 Key 是否存在,然后如果 key 存在则进行插入不存在。但是,这两个操作不是线程安全的,在 ContainsKey 的查询和 Add 操作之间,另一个线程可能已经完成了该键的插入,因此存在竞争条件。

换句话说,集合的操作是线程安全的——但它的使用却不一定。在这种情况下,需要转换回传统的锁定技术(互斥锁/监视器/信号量...)以实现线程安全,因此并发收集在多线程安全方面没有为您带来任何好处(但性能可能更差) .

【讨论】:

  • 我也是这么想的。 Put-if-absent 问题,您最终可能会在您的集合中得到 2 个以前没有的对象。谢谢+1。
  • @saret:但是,提供真正线程安全使用所需的功能到底有多难?我认为如果字典不需要保存“Nothing”值,那么一切都可以使用枚举器、Long change-count 属性和类似于 Threading.Interlocked.CompareExchange 的方法来完成。添加“Nothing”值会增加一点复杂性,但不会太多。
  • @supercat,可能更好的方法是在集合本身上提供在锁内工作的方法(例如在锁内执行操作/函数),或提供诸如 InsertIfNotExists 之类的功能
  • @saret:如果提供的例程挂起,允许在锁内执行 exec 会产生死锁的可能性。如果“无”用于表示“项目不/不应该存在”,则可以从我列出的操作中合成像 AddIfNotExists 这样的操作。这并不是说拥有更多的原语没有帮助,但我认为具有列出函数的集合可以通过包装器基本上完成线程安全集合可能想要的所有事情,前提是争用不太高(CompareExchange-基于的操作可能会饿死)。
  • @supercat- 如果提供的例程挂起,你手头就会遇到更大的问题 - 但是只要使用的锁是一致的并且支持递归获取(如果调用了进一步的锁方法)你不会让自己陷入僵局(假设提供的 func 有效)。我同意您的路线可能是一个不错的选择,但真正的问题是,像 .Net BCL 团队中的那些框架设计人员需要构建通用案例代码,这些代码必须支持可能不适合此路线的要求 - 一个“字典不需要保存“无”值”不是一个安全的假设
猜你喜欢
  • 1970-01-01
  • 2022-12-18
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多