并发数据结构-1.1.2 阻塞技术

原文链接译文链接,译者:周可人,校对:梁海舰

1.1.2 阻塞技术

在很多数据结构中,内存竞争所带来的不良现象和前文所说的顺序瓶颈带来的影响都可以通过使用细粒度锁机制来减小。在细粒度锁机制中,我们用多个粒度较小的锁来保护数据结构中的不同部分。这样做的目的是允许并发操作在它们不访问数据结构的相同部分时并行执行。这种方法也可以用于避免独立内存位置访问的额外竞争。在一些数据结构中,这种现象经常发生;举个例子,在哈希表中,对那些被哈希到不同哈希桶中的值的操作自然访问的是数据结构中的一部分。

对其他的数据结构,如基于锁的共享计数器,怎样减少竞争和降低顺序瓶颈并没有那么清晰,因为抽象地来说,所有的操作都对数据结构的同一部分做修改。一种处理竞争的方法是将所有操作分散在不同时间,从而使每一个操作在不同的时段访问计数器。一种广泛使用的处理技术被称为回退。然而,即使减少了竞争,我们的基于锁的计数器仍然缺少并行性,并且不可扩展。幸运的是,更加细致的技术可以提升可扩展性。

一种技术,被称为“合并树”,可以用来实现一个可扩展的计数器。这种技术使用了一个二叉树,每个叶子表示一个线程。树的根节点存储实际的计数器值,树的其他内部节点用来协调对根节点的访问。其中的核心思想是线程从树的叶子节点向上爬,尝试去和其他并发操作“合并”。每一次两个线程的操作在一个内部节点合并,其中一个线程(失败者),简单地在当前节点等待,直到一个返回值传递给它。另外一个线程(胜利者),朝着根节点向上,携带所有在该节点子树中合并的操作之和;一个到达根节点的胜利者线程将它的和增加到计数器中,因此所有合并的操作增长被加到计数器中。之后这个胜利者在树中下降,分发一个返回值给每一个之前合并的,处在等待状态的失败者线程。这些返回值被分发下来,这样的效果就好像所有增长操作都在根计数器被修改的时刻一个接着一个的执行。

在合并树中,失败者在等待胜利者时所使用的技术对性能而言是重要的。一个失败者采用重复地读取一个树节点的一个内存地址的方式操作等待,这被称为自旋。在缓存一致性多核处理器中,一个重要的推论是这个位置会位于运行失败者操作的处理器的本地缓存,直到胜利者操作报告结果。这意味着等待的失败者并没有产生任何不必要的,并且可能降低胜利者性能的内存流量。这种等待被称为本地自旋,且已经被证实对提升可扩展性来说至关重要。

在所谓的非一致性内存访问(NUMA)架构中,处理器访问他们的共享存储中本地存储部分要比访问其他处理器的部分要快的多。在这样的架构中,数据布局——合并树中节点在内存中的分布方式——对性能有着显著的影响。将树的叶子节点存放在处理相应线程的处理器附近可以提升性能。(我们假设线程是和处理器静态绑定的)

数据布局的问题也对缓存一致性多核处理器上并发数据结构的设计有影响。回顾合并树的一个作用是减少独立内存位置的竞争,从而提升性能。然而,因为缓存一致性多核处理器用缓存行大小的块管理内存,如果两个线程访问不同内存区域,落在了相同的缓存行中,会和他们访问同一块内存地址一样受到性能影响。这种现象被称为伪共享,这是一个常见的,令人困惑的性能问题。

在减少独立内存位置的竞争,用本地自旋减少内存流量,允许操作并行执行之后,用合并树实现的,随着并发线程的数量扩展的计数器,比单锁版本的计数器要好的多。如果所有线程被用于不断合并,那么一个P宽度的树允许P个线程在O(logP)个(合并树中的)上升和下降的操作之后,返回P个值,提供了O(P/logP)的加速比。

尽管使用合并树的方式有很多优点,但是它也有一些不足之处。合并树需要限定P个线程访问计数器,并且需要O(P)的空间。虽然它在高负载下能提供更高的吞吐量,即在树被大量线程访问时,但它在低负载访问时的最好性能是差的:它必须遍历树中的O(logP)个节点,然而一个基于单锁的fetch-and-inc操作在常数时间内可以完成。此外,如果一个线程因为它在一个胜利者线程离开树向上后马上到达导致合并失败,那么它必须等待,直到胜利者返回才能继续向上。如果上升的胜利者,失败者,以及之后的上升线程之间的协调处理错误,那么可能会导致死锁:线程可能以循环的方式互相阻塞,没有一个可以继续执行。避免死锁显著地增加了设计正确,并且高效的阻塞并发数据结构的复杂性。

总的来说,如果在使用足够的阻塞来达到正确,和尽量降低阻塞来允许并发操作并行执行之间达到平衡,阻塞数据结构可以提供强大的,高效的实现。 

时间: 2024-09-23 07:54:46

并发数据结构-1.1.2 阻塞技术的相关文章

并发数据结构-1.1.3 非阻塞技术

原文链接,译文链接,译者:Noodles,校对:周可人 1.1.3 非阻塞技术 正如前面讨论的那样,非阻塞实现主要目的是为了消除由锁带来的相关问题,为了形式化研究这一概念,多种非阻塞演进条件已经在相关文献有所研究了,如wait-freedom演进条件,lock-freedom演进条件,和obstruction-freedom演进条件.满足wait-free演进条件的操作是指在执行自身包含的有限步骤之后,保证操作必须完成,而不用考虑其他操作发生的时序,满足lock-free演进条件的操作是指在执行

并发数据结构-1.7 查找树

原文链接,译文链接,译者:iDestiny,校对:周可人 任何查找树的并发实现都可以通过用一个独占锁保护来完成.通过使用读写锁对并发性能有一定提升,读写锁允许所有只读(查找)操作并发地执行,因为读操作是以共享模式持有锁,然而更新(插入或删除)操作持有独占模式的锁,从而排斥其他所有操作.如果更新操作比较少,这还能接受,但是只要有适量的更新操作,那么更新操作所持有的独占锁将产生线性的瓶颈,从而大大降低性能.通过使用细粒度的锁策略--比如每个节点一个锁,而不是整棵树使用同一个锁--这样我们进一步地提升

并发数据结构- 1.1.1 性能

原文链接,译文链接,译者:俞升兵,校对:周可人 1.1.1 性能 一个运行在P个处理上的应用程序的加速度是它在单个处理器上的执行时间和在P个处理器的执行时间的比值.这是一种评价应用程序对于机器资源利用程度的衡量.理想情况下,我们想要的结果是线性加速度:当我们使用P个处理器的时候,我们希望可以获得P的加速度(译者注:例如一个应用程序在单处理器的执行时间是10秒,那么在双处理的执行时间理想情况下是5秒).加速度随着P一起增加的数据结构我们称之为可扩展的数据结构.在设计可扩展的数据结构的时候,我们必须

并发数据结构- 1.8 优先队列&1.9 总结

原文链接,译文链接,译者:郭振斌,校对:周可人 1.8 优先队列 并发的优先队列是一个可线性化到顺序优先队列的数据结构,能够通过常用的优先队列语义提供insert和delete-min操作. 基于堆的优先队列 许多文献中提到的并发优先队列结构,其实是本书前面提到的可线性化堆结构.再一次的,这种结构的基本思想是在个别堆节点上使用细粒度锁,使线程在并行下也能够尽可能的访问数据结构的不同部分.设计这种并发堆的关键问题,在于传统自底向上的insert和自顶向下的delete-min操作有可能造成死锁.B

招募译者翻译并发数据结构

什么是并发数据结构? 引用wiki上的定义 In computer science, a concurrent data structure is a particular way of storing and organizing data for access by multiple computing threads (or processes) on a computer. 简而言之,并发数据结构即允许多线程同时访问(读和写)的数据结构. 并发数据结构中的算法的设计原则主要分两大类,li

并发数据结构-1.1 并发的数据结构的设计

原文链接,译文链接,译者:董明鑫,校对:周可人 随着多个处理器共享同一内存的机器在商业上的广泛使用,并发编程的艺术也产生了巨大的变化.当前的趋势向着低功耗芯片级多线程(CMT)发展,所以这样的机器一定会更加广泛的被使用. 共享内存多处理器是指并发的执行多个线程的系统,这些线程在共享的内存中通过数据结构通讯和同步.这些数据结构的效率对于性能是很关键的,而目前熟练掌握为多处理器机器设计高效数据结构这一技术的人并不多.对大多数人来说,设计并发的数据结构比设计单线程的难多了,因为并发执行的线程可能会多种

并发数据结构-1.5 链表

原文链接,译文链接,译者:huavben,校对:周可人 考虑支持插入,删除和查找操作的并发数据结构实现.如果这些操作只处理键值(译者注:而不处理具体值),这样的数据结构会是一个集合.如果一个数据值与每一个键关联起来,我们就得到了一部数据字典.由于他们都是密切相关的数据结构,一个并发的集合通常能够经过适当修改来实现一部字典.在接下来的三个小节中,我们将专注于利用linked lists,hash tables,和trees这三种不同的数据结构来实现集合. 试想我们使用一个linked list去实

并发数据结构-1.1.4 复杂度测量&1.1.5 正确性

原文链接,译文链接,译者:张军,校对:周可人 1.1.4 复杂度测量 一个被广泛研究的方向是在理想化模型,如并行随机存取机上分析并发数据结构和算法的渐进复杂度[35, 122, 135].然而,很少有将这些数据结构放在一个真实的多处理器上进行建模的.这里有多种原因,大部分原因跟系统硬件架构与线程异步执行的相互作用有关.想想组合树(combining tree)的例子,虽然我们能通过计算(指令数)得到O(P/logP)的加速比,但这无法反映在实证研究中[52, 129].真实世界的行为是被上述其他

【转载】并发数据结构

本文转载自http://shift-alt-ctrl.iteye.com/blog/1841084   请首先参考:http://shift-alt-ctrl.iteye.com/blog/1839142 一.BlockingDeque阻塞双端队列(线程安全): 注意ArrayDeque和LinkedList仅仅扩展了Deque,是非阻塞类型的双端队列. BlockingQueue单向队列,其内部基于ReentrantLock + Condition来控制同步和"阻塞"/"唤