并发数据结构-1.4 池

原文链接译文链接,译者:huavben,校对:周可人

实现高效并发栈和队列的大部分挑战来自于一个被插入的元素可以被删除这一需求。并发池是一种支持插入和删除操作的数据结构,它允许删除操作移除任何一个已经被插入的,并且没有在随后被删除的元素。这样的弱需求提供了提高并发性能的机会。

一个高效的并发池可以使用任意静态一致的计数器来构建。在这样的并发池中,元素被置于数组当中,fetch-and-inc操作决定插入操作在哪个位置存储元素,同样的,fetch-and-inc操作决定删除操作在哪个位置获得元素。每一个数组元素都包含了一个表示满/空的比特位或者等效的机制,来表明在相应的位置,将要删除的元素已经存在。在这种策略下,使用combining tree,combining funnel,counting network,diffracting tree中的任何一个技术都可以并行化共享技术器,解决这一主要的瓶颈来创建出一个高吞吐量的共享并发池。此外,一个类似栈的池可以使用一个允许增加和减少操作的计数器来实现,然后利用以上技术中的一种来并行化.

最后,在前文中说讨论的消除技术(elimination technique)可以使用在利用combining tree,combining funnel,counting network,diffracting tree等技术实现的并发池中:假如在树中插入和删除操作相遇,那么删除操作可以获取插入操作插入的值,之后两个操作就不用继续遍历这个结构树。这项技术在高负载下能表现出良好的高性能.

上述的这些实现的缺点是,它们在低复杂的情况下性能糟糕。而且,在做负载分布工作的时候,这些实现并不允许我们如同那些专门为负载分布设计的池一样,得到具体的位置信息。

负载均衡算法涉及到一个任务池集合,每个任务池中有一些工作单元,其中每一个任务池都被本地化到一个处理器上。当线程创建了工作项之后就把它们置于本地化的任务池中,依靠负载均衡算法确保任务池中的工作数量都是均衡的。这样就避免了其他处理器仍然忙碌于自己工作的时候,有些处理器却处于空闲状态。大体上有两种解决这类问题的算法:工作共享(work sharing )和工作窃取(work stealing )。在工作共享方案中,每一个处理器尝试不断地将自身任务池中的工作卸下,放到其他任务池中。在工作窃取模式下,一个线程已经完成了自己的工作后就会去窃取其他任务池中的工作去执行。两种方案通过随机选择任务池来平衡工作或窃取工作。

传统工作窃取技术是Arara等人提出的,它基于一个无锁结构的双向队列。该双向队列只允许一个线程(该任务池的本地线程)在队列的一端进行操作,在另一端只允许弹出操作,并且允许在这一端的并发弹出操作在线程相互干扰的情况下中止。在这样的约束下,双向队列适合于任务窃取,并且这种约束允许一种简单的实现方式,即本地线程用低开销的load and store操作来进行插入和删除,只有当它和其他非本地删除线程竞争队列里的最后一个任务的时候,才会依赖昂贵的CAS操作。

在一些案例中已经清晰表明一次窃取多个任务的效果是令人满意的。一个非阻塞的多任务窃取算法由Hendler与Shavit在PODC(2002)上提出。在一些案例中同样表明通过使用 同类工作项信息来决定窃取那些工作是可取的。另外在SPAA(2000)上,Acar等人提出了一种位置引导(locality-guided)的窃取算法.

文章转自 并发编程网-ifeve.com

时间: 2024-09-29 05:15:18

并发数据结构-1.4 池的相关文章

并发数据结构- 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去实

Java并发编程:线程池的使用(转)

Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间. 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果.今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPool

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

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

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

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

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

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

并发数据结构:迷人的原子

随着多核CPU成为主流,并行程序设计亦成为研究领域的热门. 要想利用多核/多路CPU带来的强大功能,通常使用多线程来开发应用程序.但是要想拥有良好的硬件 利用率,仅仅简单的在多个线程间分割工作是不够的.还必须确保线程大部分时间在工作,而不是在等待 工作或等待锁定共享数据结构. 在不止一个线程访问共享数据时,所有线程都必须使用同步.如果线程间不进行协调,则没有任务可 以真正并行,更糟糕的是这会给程序带来毁灭性的错误. 现在让我们来看一下在.NET和D语言中的标准同步手段-锁定..NET下我们使用l