C++双重检测实现单例的潜在问题

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值