#include <pthread.h>
class single
{
public:
static single *getSinle();
private:
single() {}
~single() {}
static single *p;
static pthread_mutex_t lock;
};
single *single::p = NULL;
pthread_mutex_t single::lock = PTHREAD_MUTEX_INITIALIZER;
single *single::getSinle()
{
if (p = NULL){
pthread_mutex_lock(&lock);
if(p==NULL){
p=new single();
}
pthread_mutex_unlock(&lock);
}
}
问题出在p=new single();
这一句,实际上new的动作分为三步;
第一步:为Singleton对象分配一片内存
第二步:构造一个Singleton对象,存入已分配的内存区
第三步:将pInstance指向这片内存区.
但编译器可能并不按这个顺序来执行,可能先执行1,3,再执行2。
如果先执行了1,3,此时该线程挂起,另一个线程来检测,就会访问一个还没有构造的内存。出现问题。
解决方法1:
C++11后有一种更好的实现方法,利用静态局部变量,因为C++11规定当一个线程在初始化一个变量时,其他线程必须等到初始化完成才能访问。
但是在C++11却是线程安全的,这是因为新的C++标准规定了当一个线程正在初始化一个变量的时候,其他线程必须得等到该初始化完成以后才能访问它。
class single2{
public:
single2* getsingle2();
private:
single2(){}
~single2(){}
};
single2* single2::getsingle2(){
static single2 p;
return &p;
}
解决方法2:采用内存屏障的方法
- std::atomic_thread_fence(std::memory_order_acquire);
获得是一个对内存的读操作,当前线程的任何后面的读写操作都不允许重排到这个操作的前面去。
- std::atomic_thread_fence(std::memory_order_release);
释放是一个对内存的写操作,当前线程的任何前面的读写操作都不允许重排到这个操作的后面去。
acquire 和 release 通常都是配对出现的,目的是保证如果对同一个原子对象的 release 发生在 acquire 之前的话,release 之前发生的内存修改能够被 acquire 之后的内存读取全部看到。
class single3{
public:
static single3* getsingle3();
private:
single3(){}
~single3(){}
pthread_mutex_t lock;
atomic<single3*> single3:p;
};
single3* single3::getsingle3(){
single3* temp=p.load(memory_order_relaxed);
automic_thread_fence(memory_order_acquire);
if(temp==nullptr){
pthread_mutex_lock(lock);
temp=p.load(memory_order_relaxed);
temp=new single3();
atomic_thread_fence(memory_order_release);
p.store(temp,memory_order_relaxed);
}
return p;
}