比较并交换(compare and swap, CAS),是原子操做的一种,可用于在多线程编程中实现不被打断的数据交换操做,从而避免多线程同时改写某一数据时因为执行顺序不肯定性以及中断的不可预知性产生的数据不一致问题。 该操做经过将内存中的值与指定数据进行比较,当数值同样时将内存中的数据替换为新的值。php
一个CAS操做的过程能够用如下c代码表示: [1]编程
1 int cas(long *addr, long old, long new) 2 { 3 /* Executes atomically. */ 4 if(*addr != old) 5 return 0; 6 *addr = new; 7 return 1; 8 }
在使用上,一般会记录下某块内存中的旧值,经过对旧值进行一系列的操做后获得新值,而后经过CAS操做将新值与旧值进行交换。若是这块内存的值在这期间内没被修改过,则旧值会与内存中的数据相同,这时CAS操做将会成功执行 使内存中的数据变为新值。若是内存中的值在这期间内被修改过,则通常[2]来讲旧值会与内存中的数据不一样,这时CAS操做将会失败,新值将不会被写入内存。数据结构
在应用中CAS能够用于实现无锁数据结构,常见的有无锁队列(先入先出)[3] 以及无锁堆(先入后出)。对于可在任意位置插入数据的链表以及双向链表,实现无锁操做的难度较大。[4]。多线程
ABA问题是无锁结构实现中常见的一种问题,可基本表述为:函数
对于P1来讲,数值A未发生过改变,但实际上A已经被变化过了,继续使用可能会出现问题。在CAS操做中,因为比较的可能是指针,这个问题将会变得更加严重。试想以下状况:atom
top | V 0x0014 | Node A | --> | Node X | --> ……
有一个堆(先入后出)中有top和节点A,节点A目前位于堆顶top指针指向A。如今有一个进程P1想要pop一个节点,所以按照以下无锁操做进行spa
pop() { do{ ptr = top; // ptr = top = NodeA next_prt = top->next; // next_ptr = NodeX } while(CAS(top, ptr, next_ptr) != true); return ptr; }
而进程P2在执行CAS操做以前打断了P1,并对堆进行了一系列的pop和push操做,使堆变为以下结构:线程
top | V 0x0014 | Node C | --> | Node B | --> | Node X | --> ……
进程P2首先pop出NodeA,以后又Push了两个NodeB和C,因为内存管理机制中普遍使用的内存重用机制,致使NodeC的地址与以前的NodeA一致。指针
这时P1又开始继续运行,在执行CAS操做时,因为top依旧指向的是NodeA的地址(实际上已经变为NodeC),所以将top的值修改成了NodeX,这时堆结构以下:队列
top | 0x0014 V | Node C | --> | Node B | --> | Node X | --> ……
通过CAS操做后,top指针错误的指向了NodeX而不是NodeB。
CAS操做基于CPU提供的原子操做指令实现。对于Intel X86处理器,可经过在汇编指令前增长LOCK前缀来锁定系统总线,使系统总线在汇编指令执行时没法访问相应的内存地址。而各个编译器根据这个特色实现了各自的原子操做函数。[5]