.NET 4.0 Beta 2 对协调数据结构类库进行了改进

  • Jonathan Allen
  • 赵劼

2009 年 11 月 21 日

话题:.NET语言 & 开发架构

协调数据结构(Coordination Data Structures,CDS)被设计为在并发环境下使用的组件,它也可用于构建复杂的并发框架。此外,它还包含了高级的同步工具,如 Barrier,多种线程安全的集合,以及多种构建 Future 模型的方式。

Barrier 类的作用是在多个阶段的操作之间创建同步点。在使用之前,Barrier 需要知道有多少线程将会使用它。每个线程达到检查点时,便要调用 Barrier 的 SignalAndWait 方法。这么做会引起阻塞,当所有线程都调用了这个方法后,便会同时释放所有线程。这个过程可以重复多次,每个这样的迭代都会增加其 CurrentPhaseNumber 属性。用于监控的代码可以随时检查正在使用 Barrier 的线程,以及还没有到达检查点的线程数量。CurrentPhaseNumber 为 Int64 类型,因此每个 Barrier 可以支持多达 9,223,372,036,854,775,807 个阶段(之前的 beta 版本使用 Int32,因此受限于 40 亿个阶段)。

BlockingCollection类用于生产者 / 消费者场景。它最简单的使用模式便是作为一个线程安全的队列,并且在队列为空时阻塞消费者。为了避免队列元素过多,你也可以为它设置一个最大值。在到达最大容量时,生产者便会被阻塞,直到某个消费者取出元素。当 BlockingCollection 填充完毕之后,生产者可以将其标记为完成。这样便无法添加更多的元素,同时也会释放了所有的消费者。

BlockingCollection 并非只能单独使用,多个 BlockingCollection 可以一起使用。在这种模式下,生产者和消费者可以指明为任意一个 BlockingCollection 添加或删除元素,而不在乎某个特定的集合。这么做可以充分在集合之间的实现某种负载均衡。

ConcurrentDictionary类支持原子性的添加和更新操作。为了实现这个功能,你需要向 GetOrAdd 及 AddOrUpdate 方法中传入一个委托。如果当前键不在集合中便会调用 Add 委托。如果键存在,那么便会返回对应的值,或是将其传递给 Update 委托。

原本还有计划实现一个并行链表,但是这点在 beta 2 中被取消了。Joshua Phillips 写到,他们无法为这个类在性能和可用性之间做出平衡:

在每个软件专业人士的职业生涯中,总会遇到某些情况需要放弃他们所钟爱的东西。他们的发明虽然很棒,但总会有这样那样的原因,导致这些东西失去存在的充分价值。我知道你们对 Beta 1 中的 ConcurrentLinkedList<T> 很感兴趣,但是我们打算放弃它了(虽然之前我们没有给出提示)。很不幸,经过一段时间的研究,我们发现无法同时提供很好的可用性以及性能。似乎目前已经有许多线程安全的链表实现,它们的伸缩性很好,但是这种高度伸缩能力是建立在某些假设或是一些奇怪的设计方式上的,它们总会在一些特别的情况下引起严重的性能问题。移除 CLL<T> 让我们很受伤,但是它的性能的确无法满足发布的要求。

对于需要延迟执行的函数,目前有了两个选择。如果你想使用future模型来传递那些只在需要时才调用的函数,那么可以使用Lazy类。这个类封装了一个函数,保证它只在第一次访问 Value 属性的时候才执行,以后对于 Value 属性的多次访问不会引起函数的重复调用。

第二个选项则是LazyInitializer模块。EnsureInitialized 方法是一个保证初始化完成的轻量级做法,它在(且仅在)目标变量为 null 的情况才调用委托。这保证了目标变量只会设置一次。不过,如果没有使用同步对象的话,委托可能会被几个并发线程调用多次。

查看英文原文:Beta 2 Brings Refinements to .NET’s Coordination Data Structures Library
.NET语言 & 开发架构