C++线程安全的单例模式

 
// C++11跨平台实现线程安全的DCL(Double Check Lock)单例,类似Java的volatile实现内存屏障,阻止CPU指令重排序
class Single {
    Single(){}
    Single(const Single& other) = delete;
    Single& operator=(const Single& other) = delete;
    static std::atomic<Single*> m_instance;
    static std::mutex m_mutex;
public:
    static Single* GetInstance() {
        Single* tmp = m_instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);            // 获取内存fence
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> locker(m_mutex); // 加锁, RAII
            tmp = m_instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Single;
                std::atomic_thread_fence(std::memory_order_release);    // 释放内存fence
                m_instance.store(tmp, std::memory_order_relaxed);
            }
        }// 结束后,锁自动释放

        return tmp;
    }
};
std::mutex Single::m_mutex;
std::atomic<Single*> Single::m_instance;
// 局部static变量实现泛型单例管理类,C++能够保证static变量只会被创建一次
template<typename T>
class Singleton {
   Singleton() = delete;
   Singleton(const Singleton& other) = delete;
   Singleton& operator=(const Singleton& other) = delete;
public:
   inline static T& GetInstance() {
       static T instance;// T必须能够执行默认初始化或提供了默认构造函数
       return instance;
   }
};
// 外部使用方法,只需要对外暴露Singleton<ClassName>就能实现对ClassName的单例管理
// 简单饿汉式,也可以使用局部static变量,这样就能等到调用GetInstance()函数时才会真正创建单例对象
class Singleton {
   Singleton() {}
   Singleton(const Singleton& other) = delete;
   Singleton& operator=(const Singleton& other) = delete;
   static Singleton m_instance;
public:
   inline static Singleton* GetInstance() {
        return &m_instance;
   }
};
// 真正定义,分配内存的地方
Singleton Singleton::m_instance;

 C++中volatile关键字和Java的volatile关键字实现的功能不一样:

   C++中volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。

  当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。

  Java中volatile关键字有2个作用:1、保证线程之间变量内存的可见性;2、阻止CPU指令重排序

原文地址:https://www.cnblogs.com/djh5520/p/14748251.html