单例模式及多线程安全(C++版)

单例模式


Code

// 单例设计模式
class sigleC {
public:
    static sigleC* getInstance() {
        if (m_instance == nullptr) {
            m_instance = new sigleC();
            static delobj cl; // 
        }
        return m_instance;    
    }
    // 通过嵌套类来实现析构
    class delobj {
    public:
        ~delobj()
        {
            if (sigleC::m_instance) {
                delete sigleC::m_instance;
                sigleC::m_instance = nullptr; // 创建的单例能一直维持到程序结束才被释放
            }
        }
    };
    void func() {
        cout << "测试" << endl;
    }
private:
    sigleC(){}
    static sigleC* m_instance;


};

// 类静态变量初始化
sigleC* sigleC::m_instance = nullptr;
int main()
{
    sigleC* p1 = sigleC::getInstance(); // 只创建一个sigleC类对象
    sigleC* p2 = sigleC::getInstance();

    return 0;
}


那么为什么不在单例类的析构函数中直接释放m_instance,而在类中嵌套另一个类?

原因
析构函数是在类的某一个对象离开作用域时自动调用的,如果在程序中创建了一个单例类对象obj1,之后obj1离开了它的作用域,后来我又创建了一个单例类对象obj2,期望是后来创建的obj2的内容和原来创建obj1时的m_instance是一样的。如果是在单例类的析构函数中释放m_instance然后置为null的话,则我后面obj2所得到的instance又是重新new出来的,和原来obj1的instance不是同一个!
总结:用老式的方式所

多线程安全问题:双重锁定

#include <iostream>
#include <mutex>
using namespace std;
std::mutex resource_mutex;

class sigleC {
public:
    static sigleC* getInstance() {
        if (m_instance == nullptr) { // 双重锁定机制
            std::unique_lock<std::mutex> mymutex(resource_mutex);
            if (m_instance == nullptr) {
                m_instance = new sigleC();
                static delobj cl;
            }
        }
        return m_instance;
    }
    // 通过嵌套类来实现析构
    class delobj {
    public:
        ~delobj()
        {
            if (sigleC::m_instance) {
                delete sigleC::m_instance;
                sigleC::m_instance = nullptr; // 创建的单例能一直维持到程序结束才被释放
            }
        }
    };
    void func() {
        cout << "测试" << endl;
    }
private:
    sigleC() {}
    static sigleC* m_instance;
};

// 线程函数
void mythread()
{
    cout << "开辟线程开始" << endl;
    sigleC* p1 = sigleC::getInstance(); // 只创建一个sigleC类对象
    cout << "开辟线程结束" << endl;
}

// 类静态变量初始化
sigleC* sigleC::m_instance = nullptr;

int main()
{
    std::thread t1(mythread);
    std::thread t2(mythread);
    t1.join();
    t2.join();
    sigleC::getInstance()->func();
    return 0;
}



双重锁定亦然存在问题,会带来reorder现象


正常的指令序列 m_instance = new sigleC();

  1. 分配内存;
  2. 调用构造器
  3. 将指针返回值传递给m_instance,我们以为cpu会这么做,但是实际上可能会发生2、3步骤交换的情况,导致双重锁定失效

解决办法如下:

å¨è¿éæå¥å¾çæè¿°
 

方法2:std::call_once()
 

std::mutex resource_mutex;
std::once_flag g_flag; // 这是个系统定义的标记
 
class sigleC {
public:
    static void createInstance() {  // 只被调用一次
        m_instance = new sigleC();
        static delobj cl;
    }

    static sigleC* getInstance() {
        std::call_once(g_flag, createInstance);  // 可以把g_flag想象成一把锁
        return m_instance;
    }
    // 通过嵌套类来实现析构
    class delobj {
    public:
        ~delobj()
        {
            if (sigleC::m_instance) {
                delete sigleC::m_instance;
                sigleC::m_instance = nullptr; // 创建的单例能一直维持到程序结束才被释放
            }
        }
    };
    void func() {
        cout << "测试" << endl;
    }
private:
    sigleC() {}
    static sigleC* m_instance;
};

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值