zoukankan      html  css  js  c++  java
  • 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 }

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

  • 相关阅读:
    解读基础设施即代码
    在那江南烈日与阵雨中-江南100赛记
    写在2017年环汶川越野赛之前
    阿里巴巴Java开发手册评述
    一个程序员的2016年总结
    面向对象的思考过程第四版译者序
    以敏捷的方式运作一所大学
    敏捷团队中的QA由来
    Spring Batch在大型企业中的最佳实践
    Java基础 awt Button 鼠标放在按钮上背景颜色改变,鼠标离开背景颜色恢复
  • 原文地址:https://www.cnblogs.com/chen-cs/p/13278002.html
Copyright © 2011-2022 走看看