为什么C#中没有并发集合?

我试图概述C#中集合背后的线程安全理论。

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

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

我不想重新发明轮子! (我不是一个multithreading的大师,绝对不会低估这有多难)。

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

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

安德鲁的答案在.NET 4.0之前如何处理集合方面是完全正确的 – 对于大多数用途,我只是在访问“普通”共享集合时适当锁定。 但是,并发集合使得使用生产者/消费者队列等变得容易。

C#提供了多种方法来处理跨多个线程的集合。 为了更好地记录这些技术,我建议您从集合和同步(线程安全)开始 :

默认情况下,Collections类通常不是线程安全的。 多位读者可以放心地阅读该系列; 但是,对集合的任何修改都会为访问集合的所有线程(包括读取器线程)生成未定义的结果。

可以使用以下任何方法使集合类成为线程安全的:

  • 使用Synchronized方法创建一个线程安全的包装器,并通过该包装器专门访问该集合。
  • 如果类没有Synchronized方法,则从类派生并使用SyncRoot属性实现Synchronized方法。
  • 访问集合时,在SyncRoot属性上使用锁定机制(如C#中的lock语句(Visual Basic中的SyncLock))。

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

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

(这不完全正确,因为一些集合提供了一个Synchronized方法来从非线程安全集合返回线程安全集合,因此有一些线程安全集合……)

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

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