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 }

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

  • 相关阅读:
    Android 修改应用程序字体
    Activity A 跳转到Activity B 生命周期
    Android调用系统设置
    最近遇到adb connection 问题,总结一下
    今日写一篇散文 Textview settext 方法不能放入 int 参数 不然报错!
    计时线程Runnable和Handler的结合
    JMF 下载安装与测试 测试成功
    基本数据类型的介绍及转换,基本数据类型与字符串之间转换,字符串与字符数组之间转换以及字符串与字节数组之间转换
    超实用的Eclipse快捷键大全(解密为什么他们的代码写的又快又好~)
    JDK的下载、安装及Eclipse安装详细教程(内附:网盘win64版JDK安装包)
  • 原文地址:https://www.cnblogs.com/chen-cs/p/13278002.html
Copyright © 2011-2022 走看看