• Singleton单例模式


    模式定义

      保证一个类只能生成一个实例对象。

    单线程版本

     1 class Singleton{
     2 private:
     3     // 私有化构造函数和拷贝构造函数
     4     Singleton();
     5     Singleton(const Singleton& other);
     6 public:
     7     // 静态对象指针和静态获取对象指针函数
     8     static Singleton* getInstance();
     9     static Singleton* m_instance;
    10 };
    11 // 初始化
    12 Singleton* Singleton::m_instance=nullptr;
    13 
    14 //线程非安全版本
    15 Singleton* Singleton::getInstance() {
    16     if (m_instance == nullptr) {
    17         m_instance = new Singleton();
    18     }
    19     return m_instance;
    20 }
    21 
    22 //线程安全版本,但锁的代价过高
    23 Singleton* Singleton::getInstance() {
    24     Lock lock;
    25     if (m_instance == nullptr) {
    26         m_instance = new Singleton();
    27     }
    28     return m_instance;
    29 }

    多线程版本

    class Singleton{
    private:
        // 私有化构造函数和拷贝构造函数
        Singleton();
        Singleton(const Singleton& other);
        mutex  mymutex;  //互斥量
    public:
        // 静态对象指针和静态获取对象指针函数
        static Singleton* getInstance();
        static Singleton* m_instance;
    };
    // 初始化
    Singleton* Singleton::m_instance=nullptr;    

    线程安全版本,但锁的代价过高

    1 //线程安全版本,但锁的代价过高
    2 Singleton* Singleton::getInstance() {
    3     unique_lock<mutex> mylock(mymutex);
    4     if (m_instance == nullptr) {
    5         m_instance = new Singleton();
    6     }
    7     return m_instance;
    8 }

    双检查锁版本(会出错)

     1 //双检查锁
     2 // 但由于内存读写reorder不安全, 编译器的优化有关
     3 // 在汇编层面m_instance = new Singleton()语句可能的执行顺序是
     4 // 先分配内存地址,再执行构造函数,再赋值给内存,导致第二次检查结果为false,但是这时对象还没创建
     5 Singleton* Singleton::getInstance() {
     6     // 这里的检查,在已经创建了单例对象后,就没有必要加锁
     7     if(m_instance==nullptr){
     8         Lock lock;
     9         // 在检查一次是为了避免两个线程都通过了第一个检查,都会创建的对象的情况
    10         if (m_instance == nullptr) {
    11             m_instance = new Singleton();
    12         }
    13     }
    14     return m_instance;
    15 }

    原子操作版本

     1 //C++ 11版本之后的跨平台实现 (volatile)
     2 // 或者使用原子操作
     3 std::atomic<Singleton*> Singleton::m_instance;
     4 std::mutex Singleton::m_mutex;
     5 
     6 Singleton* Singleton::getInstance() {
     7     Singleton* tmp = m_instance.load(std::memory_order_relaxed);
     8     std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
     9     if (tmp == nullptr) {
    10         std::lock_guard<std::mutex> lock(m_mutex);
    11         tmp = m_instance.load(std::memory_order_relaxed);
    12         if (tmp == nullptr) {
    13             tmp = new Singleton;
    14             std::atomic_thread_fence(std::memory_order_release);//释放内存fence
    15             m_instance.store(tmp, std::memory_order_relaxed);
    16         }
    17     }
    18     return tmp;
    19 }

      综上,在多线程中,推荐使用原子操作版。

  • 相关阅读:
    Qt Q3DScatter中元素的移动和旋转
    Qt QtDataVisualization Q3DScatter绘制散点图
    Qt qwtplot3d根据时间采集多条曲线
    Qt QWT3D 之 三维动态曲线的实现
    Qt 数据可视化之3D图形
    Vagrant在虚拟机Centos7上安装Docker
    VirtualBox+Vagrant环境搭建
    VMware安装Centos7超详细过程(图文)
    上传excel表格批量导入数据到数据库
    IP协议包头分析
  • 原文地址:https://www.cnblogs.com/chen-cs/p/13278002.html
Copyright © 2020-2023  润新知