leveldb(一):AtomicPointer

来源:互联网 发布:西南大学网络教育2017 编辑:程序博客网 时间:2024/06/16 00:29

AtomicPointer 简介

AtomicPointer 是 leveldb 提供的一个原子指针操作类,使用了基于内存屏障的同步访问机制,这比用锁和信号量的效率要高。

源文件位置:leveldb/port/atomic_pointer.h
它的实现主要分为两种:

(一)利用std::atomic实现AtomicPointer

  std::atomic对int, char, bool等数据结构进行原子性封装,在多线程环境中,对std::atomic对象的访问不会造成竞争-冒险。利用std::atomic可实现数据结构的无锁设计。(对于std::atomic的详细分析可以参见我的c++11分类里的C++11开发中的Atomic原子操作)

class AtomicPointer { private:  std::atomic<void*> rep_; public:  AtomicPointer() { }  explicit AtomicPointer(void* v) : rep_(v) { }  // 使用内存屏障的读操作,即同步读  inline void* Acquire_Load() const  {    return rep_.load(std::memory_order_acquire);  }  // 使用内存屏障的读操作,即同步写  inline void Release_Store(void* v)   {    rep_.store(v, std::memory_order_release);  }  inline void* NoBarrier_Load() const   {    // 不使用内存屏障的读操作,即不同步的读操作    return rep_.load(std::memory_order_relaxed);  }  inline void NoBarrier_Store(void* v)   {    // 同上,是不同步的写操作    rep_.store(v, std::memory_order_relaxed);  }};

(二)利用内存屏障来实现AtomicPointer

class AtomicPointer {  private:    void* rep_;  public:    AtomicPointer() { }    explicit AtomicPointer(void* p) : rep_(p) {}    // 不使用内存屏障的读操作,即不同步的读操作    inline void* NoBarrier_Load() const { return rep_; }    // 同上,是不同步的写操作    inline void NoBarrier_Store(void* v) { rep_ = v; }    // 使用内存屏障的读操作,即同步读    inline void* Acquire_Load() const     {      void* result = rep_;      // 添加一个内存屏障,后面会有原理介绍      MemoryBarrier();      return result;    }    // 使用内存屏障的写操作,即同步写    inline void Release_Store(void* v)     {      MemoryBarrier();      rep_ = v;    }};
// Define MemoryBarrier() if available// Windows on x86#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)// windows.h already provides a MemoryBarrier(void) macro#define LEVELDB_HAVE_MEMORY_BARRIER// Gcc on x86#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__)inline void MemoryBarrier() {  __asm__ __volatile__("" : : : "memory");}#define LEVELDB_HAVE_MEMORY_BARRIER#endif

从上面可以看出windows平台已经定义过MemoryBarrier(void)的宏,可以直接使用。

而linux平台的gcc则通过我们自己内联一条汇编来自定义MemoryBarrier。
asm volatile(“” : : : “memory”);
这里的volatile主要是用来防止编译器优化的。而这里的优化是针对代码块而言的,使用嵌入式汇编的代码分成三块:
1、嵌入式汇编之前的c代码块
2、嵌入式汇编代码块
3、嵌入式汇编之后的c代码块
这里volatile就是告诉编译器:不要因为性能优化而将这些代码重排,我需要清清爽爽的保持这三块代码块的顺序(代码块内部是否重排不是这里的volatile管辖范围了)。

MemoryBarrier是添加一个内存屏障的函数,当这个函数之前的代码修改了某个变量的内存值后,其他 CPU 和缓存 (Cache) 中的该变量的值将会失效,必须重新从内存中获取该变量的值。
总的来说,AtomicPointer 这个类是为了让我们以更高的效率实现原子性的访问。

什么是内存屏障 ?

内存屏障是同步的一种方法,类似于锁和信号量,但是它有更高的效率。内存屏障深入研究的水很深,这里只介绍它的基本用途和使用,而不会深入。(如果你想深入了解内存屏障,请看我的 内核之同步 分类里的 memory barrier)

基本用途

避免编译器优化指令
有些编译器默认会在编译期间对代码进行优化,从而改变汇编代码的指令执行顺序,如果你是在单线程上运行可能会正常,但是在多线程环境很可能会发生问题(如果你的程序对指令的执行顺序有严格的要求)。

而内存屏障就可以阻止编译器在编译期间优化我们的指令顺序,为你的程序在多线程环境下的正确运行提供了保障,但是不能阻止 CPU 在运行时重新排序指令。

如果你想实现下面这样的功能,那你可以考虑内存屏障:
修改一个内存中的变量之后,其余的 CPU 和 Cache 里面该变量的原始数据失效,必须从内存中重新获取这个变量的值。
这保证了这个变量对 CPU 和 Cache 是「可见的」,leveldb 就使用了这个特性