无锁队列有关问题【1】

来源:互联网 发布:2016全国酒驾车祸数据 编辑:程序博客网 时间:2024/05/21 06:32

无锁队列有关问题【一】

实现无锁队列的关键点有二:

1、各个平台的原子操作或者说CAS原语;

2、ABA问题的理解和解决。

首先来说说这个CAS原语,所谓CAS(Compare And Swap)即比较并交换,在 Intel 处理器中,比较并交换通过指令的 cmpxchg 系列实现。CAS有三个操作数: 内存位置(V)、预期原值(A)和新值(B)。如果内存位置V的值与预期A原值相匹配,那么处理器会自动将该位置值更新为新值B。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”CAS的C语言实现如下:

inline bool CAS2(pointer_t *addr, pointer_t &old_value, pointer_t &new_value)
{
bool ret;
__asm__ __volatile__(
"lock cmpxchg16b %1;\n"
"sete %0;\n"
:"=m"(ret),"+m" (*(volatile pointer_t *) (addr))

:"a" (old_value.ptr), "d" (old_value.tag), "b" (new_value.ptr), "c" (new_value.tag));
return ret;
}

这其中包括了内联汇编代码(这个可以去看看AT&T汇编语法),其中能够支持多线程的并行安全执行的秘诀就在 "lock cmpxchg16b %1;\n"这句中的“lock”,这个lock就和我们基本多线程编程中的锁相当,只不过,这个lock不再是普通的锁,它锁的是地址总线,在多核编程情景下,当已经有CPU的线程A已经访问某地址时,这时地址总线会被锁定,其他CPU核心的线程B无法再访问当前地址,直到A访问完毕之后,其他线程如B才可能访问该地址,这样就保证了线程A访问该地址的原子性操作。

下面是一些有关锁的实现方法:(代码来自WikipediaCompare And Swap词条)【内存位置V的值与预期A原值相匹配,那么处理器会自动将该位置值更新为新值B】意思就是说,看一看内存*reg里的值是不是oldval,如果是的话,则对其赋值newval

1

2

3

4

5

6

7

intcompare_and_swap (int* reg, intoldval,intnewval)

{

  intold_reg_val = *reg;

  if(old_reg_val == oldval)

     *reg = newval;

  returnold_reg_val;

}

这个操作可以变种为返回bool值的形式(返回 bool值的好处在于,可以调用者知道有没有更新成功):

1

2

3

4

5

6

7

8

boolcompare_and_swap (int*accum,int*dest,intnewval)

{

  if( *accum == *dest ) {

      *dest = newval;

      returntrue;

  }

  returnfalse;

}

CAS相似的还有下面的原子操作:(这些东西大家自己看Wikipedia吧)

· Fetch And Add,一般用来对变量做 +1 的原子操作

· Test-and-set,写值到某个内存位置并传回其旧值。汇编指令BST

· Test and Test-and-set,用来低低Test-and-Set的资源争夺情况

注:在实际的C/C++程序中,CAS的各种实现版本如下:

1GCCCAS

GCC4.1+版本中支持CAS的原子操作(完整的原子操作可参看 GCC Atomic Builtins

1

2

bool__sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)

type __sync_val_compare_and_swap (type *ptr, type oldval type newval, ...)

2WindowsCAS

Windows下,你可以使用下面的Windows API来完成CAS:(完整的Windows原子操作可参看MSDNInterLocked Functions

1

2

3

InterlockedCompareExchange ( __inoutLONGvolatile*Target,

                                __inLONGExchange,

                                __inLONGComperand);

3) C++11中的CAS

C++11中的STL中的atomic类的函数可以让你跨平台。(完整的C++11的原子操作可参看 Atomic Operation Library

1

2

3

4

5

6

template<classT >

boolatomic_compare_exchange_weak( std::atomic<T>* obj,

                                   T* expected, T desired );

template<classT >

boolatomic_compare_exchange_weak(volatilestd::atomic<T>* obj,

                                   T* expected, T desired );


再来讲讲这个ABA问题。在进行CAS操作时,因为在更改V值之前,CAS主要是通过访问V的值是否仍然和A相等,所以,在第一次读取V以及对V执行CAS操作之前,如果有其他线程将V的值先从A改为B,而另外的线程又将V的值从B改回了A,这样会使CAS算法混乱。显然,在这种情况下,CAS的操作会成功,这类的问题称为ABA问题。要解决这类问题,就是不再重用A,通常的做法是用标记或版本编号与进行CAS操作的每个值相关联,并原子的更新值和标记

0 0
原创粉丝点击