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