多核分布式队列的实现:"偷"与"自私"的运用

在讨论本文的正题前,不得不先说一些闲话,嫌哆嗦者可以跳过"前言"部分不读。
  • 1. 前言
在发表了"老子是伟大的多核计算科学家" (链接:[url]http://blog.csdn.net/drzhouweiming/archive/2008/11/07/3246254.aspx[/url],为叙述方便,后面将这篇文章简称为"老子")一文后,褒扬者有许多,但是也引来了许多板砖。当然大部分板砖都只是泛泛的批评,没有任何内容。不过有些人觉得似乎有些牵强附会,倒是引起了我的注意,确实这类文章可能确实容易给人牵强附会的感觉。
需要说明的是,本人并没有觉得它是牵强附会的。首先申明一下,我并不是研究哲学的,也没有详细研究过老子的《道德经》,但是我在设计多核算法时,确实受到了《道德经》中的思想启发。举两个例子如下:
第一个例子是在设计多核查找算法(链接:[url]http://blog.csdn.net/drzhouweiming/archive/2008/10/27/3159501.aspx[/url]) 时,最初我是用AVL树作为多级查找结构的子查找结构的,当时觉得AVL树肯定会比数组更好,因为对稍微大一点的数组进行插入删除的效率非常低,只能用在 很少数据的表上,不能对大量数据的表进行管理。记得有一天看电视时,凑巧看到在讲老子的小国寡民思想,谈到了结绳而治的问题,受此启发,对AVL树比数组 更好的想法产生了怀疑,于是试着将查找子结构改为用最原始的数组来实现,结果发现即使对上百万个规模的数据的表进行处理,综合性能也比用AVL树更好。
第二个例子是在设计多核分布式内存管理算法时,采用了"抢"的方法,使得分配和释放内存不需要使用锁。这也是受《道德经》中的"无为"及"大道自 然"的思想影响,因为之前已经发现"贪心"、"自私"、"偷"这几种人性的本能在算法中得到广泛使用,既然连"偷"都在多核算法中得到使用,那么它的孪生 兄弟"抢"应该也可以在多核算法中得到使用,本着此思想,后来终于发现可以将"抢"的思想用在多核分布式内存管理算法中,大大提高共享内存分配和释放的效 率。
对老子《道德经》的解释,历来有各种不同的解释。既然有些人只是在理论层面都可以进行解释,我现在把它的部分思想用到了具体的多核算法中,变成了在计算机里可以实际运行的程序,对它解释一下就变成了牵强附会的话,那么这种牵强附会我想越多越好。
闲话少叙,言归正传,下面就来谈一个使用"偷"与"自私"的方法实现的多核分布式队列的详细实例,以看看如何将看似泛泛而谈的思想变成可以运行的程序的。
  • 2. 分布式队列的基本概念
在"多核编程中的条件同步模式"(链接: [url]http://softwareblogs-zho.intel.com/2009/01/14/845/[/url])这篇文章中,讲到了如何减少共享队列中的锁的使用次数的具体方法,在它的基础上,可以构造出一个高效的队列池。
如果采用线程分组竞争模式(参见"多核编程中的线程分组竞争模式,链接:[url]http://blog.csdn.net/drzhouweiming/archive/2007/07/10/1684753.aspx[/url])来实现队列池,那么每组线程对应于队列池中的一个子队列,当某个线程在操作自己所属的子队列时,如果子队列为空却进行出队操作,那么此时可以从其他组线程所属的子队列中进行出队操作,这也就是"老子"一文中所说的"偷"的方法的使用。
有没有更好的方法进一步减少同步或者锁的使用呢?答案是有的。偷别人的东西总不如掏自己口袋里的东西来得方便,之所以需要"偷",乃是因为自己口袋里空空。如果大家都富裕了,口袋都鼓鼓的了,自然不需要去"偷"别人的了。
当然在计算机中,"富裕"的办法就是给每个线程赋予一个私有队列,这样每个线程可以大部分时间都操作自己私有队列,不需要同步操作,大大提高效率,这也就是"老子"一文中所说的"自私"方法的使用。
基于"偷"和"自私"两种方法,就可以设计出一个适应多核环境的分布式队列。在分布式队列中,每个操作队列的线程都有一个私有队列,另外为了解决私有队列间的负载均衡问题,还需要一个队列池来维护数据的负载均衡。
分布式队列的数据结构示意图如下:
图1:分布式队列数据结构示意图
有了上面的数据结构图,具体来实现就可以分为两个步骤:
  • 1、 实现一个队列池
  • 2、 给每个线程赋予一个私有队列
队列池的实现可以采用前面讲过方法实现,这里就不详述了,下面主要谈谈如何给每个线程赋予一个私有队列(也称作本地化队列)的详细实现方法。