线程安全集合

.NET Framework 4 引入了 System.Collections.Concurrent 命名空间,其中包含多个线程安全且可缩放的集合类。 多个线程可以安全高效地从这些集合添加或删除项,而无需在用户代码中进行其他同步。 编写新代码时,只要将多个线程同时写入到集合时,就使用并发集合类。 如果仅从共享集合进行读取,则可使用 System.Collections.Generic 命名空间中的类。 建议不要使用 1.0 集合类,除非需要定位 .NET Framework 1.1 或更低版本运行时。

.NET Framework 1.0 和 2.0 集合中的线程同步

.NET Framework 1.0 中引入的集合位于 System.Collections 命名空间中。 这些集合(包括常用的 ArrayListHashtable)通过 Synchronized 属性(此属性围绕集合返回线程安全的包装器)提供一些线程安全性。 该包装器通过对每个添加或删除操作锁定整个集合进行工作。 因此,每个尝试访问集合的线程必须等待,直到轮到它获取锁定。 这不可缩放,并且可能导致大型集合的性能显著下降。 此外,这一设计并不能完全防止争用情况的出现。 有关详细信息,请参阅泛型集合中的同步

.NET Framework 2.0 中引入的集合类位于 System.Collections.Generic 命名空间中。 它们包括 List<T>Dictionary<TKey,TValue> 等。 与 .NET Framework 1.0 类相比,这些类提升了类型安全性和性能。 不过,.NET Framework 2.0 集合类不提供任何线程同步;多线程同时添加或删除项时,用户代码必须提供所有同步。

建议使用 .NET Framework 4 中的并发集合类,因为它们不仅能够提供 .NET Framework 2.0 集合类的类型安全性,而且能够比 .NET Framework 1.0 集合更高效完整地提供线程安全性。

细粒度锁定和无锁机制

某些并发集合类型使用轻量同步机制,如 SpinLockSpinWaitSemaphoreSlimCountdownEvent,这些机制是 .NET Framework 4 中的新增功能。 这些同步类型通常在将线程真正置于等待状态之前,会在短时间内使用忙旋转。 预计等待时间非常短时,旋转比等待所消耗的计算资源少得多,因为后者涉及资源消耗量大的内核转换。 对于使用旋转的集合类,这种效率意味着多个线程能够以非常快的速率添加和删除项。 有关旋转与锁定的详细信息,请参阅 SpinLockSpinWait

ConcurrentQueue<T>ConcurrentStack<T> 类完全不使用锁定。 相反,它们依赖于 Interlocked 操作来实现线程安全性。

注意

由于并发集合类支持 ICollection,因此该类可提供针对 IsSynchronizedSyncRoot 属性的实现,即使这些属性不相关。 IsSynchronized 始终返回 false,而 SyncRoot 始终为 null(在 Visual Basic 中是 Nothing)。

下表列出了 System.Collections.Concurrent 命名空间中的集合类型。

类型 描述
BlockingCollection<T> 为实现 IProducerConsumerCollection<T> 的所有类型提供限制和阻止功能。 有关详细信息,请参阅 BlockingCollection 概述
ConcurrentDictionary<TKey,TValue> 键值对字典的线程安全实现。
ConcurrentQueue<T> FIFO(先进先出)队列的线程安全实现。
ConcurrentStack<T> LIFO(后进先出)堆栈的线程安全实现。
ConcurrentBag<T> 无序元素集合的线程安全实现。
IProducerConsumerCollection<T> 类型必须实现以在 BlockingCollection 中使用的接口。
Title 描述
BlockingCollection 概述 描述 BlockingCollection<T> 类型提供的功能。
如何:在 ConcurrentDictionary 中添加和移除项 描述如何从 ConcurrentDictionary<TKey,TValue> 添加和删除元素
如何:在 BlockingCollection 中逐个添加和取出项 描述如何在不使用只读枚举器的情况下,从阻止的集合添加和检索项。
如何:向集合添加限制和阻塞功能 描述如何将任一集合类用作 IProducerConsumerCollection<T> 集合的基础存储机制。
如何:使用 ForEach 移除 BlockingCollection 中的项 介绍了如何使用 foreach(在 Visual Basic 中是 For Each)在锁定集合中删除所有项。
如何:在管道中使用阻塞集合的数组 描述如何同时使用多个阻塞集合来实现一个管道。
如何:使用 ConcurrentBag 创建目标池 演示如何使用并发包在可重用对象(而不是继续创建新对象)的情况下改进性能。

参考

System.Collections.Concurrent