无锁数据结构(二)

无锁数据结构(二)

Andrei Alexandrescu

December 16, 2007

译者:张桂权

12/25/2007


初稿阶段,没有得到许可不得引用,否则后果自负) 

2 一句忠言

通常一篇C++论文内嵌C++代码段和实例。理想的情况是,这些代码是标准C++和Generic尽力去支撑自己的观点/理论。当书写多线程代码是,给出一个标准C++代码的实例几乎不可能。标准C++中没有Thread(线程),所以你不能编写不存的东西。与此同时,本文的代码是“pseudocode”(伪码),并不意味标准C++代码是兼容编译的。

以内存界线(memory barriers)为例。现实的代码需要其中的算法的汇编语言的转化或至少少量的C++代码,所谓的“内存界线”——处理器相关(processor-dependent)的magic,强制适当顺序的内存读和写。本文不想在无锁数据结构之外,展开内存界线的解析。如果感兴趣,你可以看看Butenhof的优秀图书[3]或一个简短的简绍[6]。本文的目标是,我们仅仅假设编译器没有做时髦的优化(比如,忽略“冗余”变量的读取,单线程下的有效优化)。从技术上讲,这是连续地始终如一(sequentially consistent)的模型,其中读和写的执行完全按照源代码中的顺序进行。

3 无等待和无锁 vs 有锁(Wait-Free and Lock-Free versus Locked)

为了明确这些术语,让我们做一些定义。一个“无等待”过程,是一个可以在有限数量的步骤中完成的,不论其它线程的相对速度如何,过程。

一个“无锁”过程,保证执行这个过程的至少一个线程的进度。也就是,有些线程可以被随意的延迟,但是保证所有线程中至少有一个线程,每一步都有进展。从统计学上来说,在一个无锁过程中,所有的线程都会有进展。

基于锁的程序不能提供以上的任何保证。如果任何一个线程在持有一个独占锁时被延迟,那么等待同一个独占的线程都不会有进展;在通常情况下,基于锁的算法会去捕获死锁——每一个等待被其它线程锁住的独占(mutex)——和活锁(livelock)——每一个试图躲避其它线程的加锁行为,简直就像走廊上的两个人都试图经过另外一个人,最后以同步中交谊舞式的左右摆动结束。我们人类以非常漂亮的微笑来终止这种尴尬的场面;然而,处理器经常喜欢这样,直到重启,把它们分开。

无等待和无锁算法享有从它们的定义中派生出来的好处:

(1)     线程杀死免疫(Thread-killing immunity):系统中任意一个线程被强行的杀死,不会延迟其它的线程。

(2)      信号免疫(Signal immunity): 通常,在信号和异步中断中,子例程,比如malloc不能被调用。这是因为正当这个子例程持有锁的时候可能发生中断。拥有无锁例程,就不会再有这种问题了:线程可以自由的交错执行。

(3)      优先反演免疫(Priority inversion immunity): 优先反演在低优先级的线程持有一个高优先级的线程需要的独占的锁时发生,在这种情况下,CPU的资源必须视为加锁特权。这是一种技巧,并且由OS的内核提供。无等待和无锁算法对这种问题有免疫。

已经进行了必要的介绍,现在让我们来分析一个精简设计的无锁实现。


---------------源文档------------------
Lock-Free Data Structures
Andrei Alexandrescu
December 17, 2007
http://erdani.org/publications/cuj-2004-10.pdf
最后一次访问时间:2007年12月30日
----------------------------------------

你可能感兴趣的:(读书笔记)