c++ singleton thread safe

c++11 static 局部变量

Foo& getInst()
{
    static Foo inst(...);
    return inst;
}

double check lock with atomic

atomic<Foo*> Foo::pinstance { nullptr };
std::mutex Foo::m_;

Foo* Foo::Instance() {
  if(pinstance == nullptr) {
    lock_guard<mutex> lock(m_);
    if(pinstance == nullptr) {
        pinstance = new Foo(); // 因为pinstance是atomic类型,并眀重载的默认实现有memoer barrier seq_cst,所以没问题。
    }
  }
  return pinstance;
}

错误实现

static Foo &getInst()
{
  static Foo *inst = NULL;
  if(inst == NULL)
  {
    pthread_mutex_lock(&mutex);
    if(inst == NULL)
      inst = new Foo(...);
    pthread_mutex_unlock(&mutex);
  }
  return *inst;    
}

这个实现初看没什么问题,但是实际是错的

因为inst = new Foo 这一句分成了三个步骤:

p = malloc(sizeof(Foo))      // malloc      1
new (p) Foo                  // call construct 2
inst = p                     // assign     3

编译器优化时或者cpu执行时有指令重排,可能会把步骤3放到步骤2前面,这时另一个线程就会读取inst不为null,但是因为步骤2还没有执行,所以还没有构造完成,另一个线程就拿到了一个有问题的对像。

一种正确的修改方案: 添加一个memory_barrier指令

static Foo &getInst()
{
  static Foo *inst = NULL;
  if(inst == NULL)
  {
    pthread_mutex_lock(&mutex);
    if(inst == NULL)
    {
      tmp = new Foo(...);
      memory_barrier              /// 添加一个memory barrier
      inst = tmp;
    }
    pthread_mutex_unlock(&mutex);
  }
  return *inst;    
}

原因参考C++ and the Perils of Double-Checked Locking how-do-you-implement-a-singleton-efficiently-and-thread-safely