为什么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安全性方面没有给您带来任何好处(但可能性能更差) 。