设计模式的理解:单例模式(Singleton)

单例模式使用的目的是 ①一个类只能初始化出一个对象 ②保证对象是线程安全的。

其做法:

1、将 构造函数 和拷贝构造函数 设置为私有 并将自身类的指针设置成静态成员,其函数方法也设置成静态方法。保证该类只创建一个对象

2、通过加锁的方式,对 对象访问加以限制

class Singleton {  
 private:
      static Singleton * instance;  
      Singleton (){}    
      Singleton(const Singleton& other);
 public:
      static Singleton* getInstance() {  
      if (instance == nullptr) {  
          instance = new Singleton();  
      }  
          return instance;  
      }
}  

以上在单线程中是可以满足一个类对象只会被初始化一次的条件。但是在多线程中 假如其中一个线程在执行   instance = new Singleton();   语句前,而另外一个刚好进入 if 语句时,这个类就会被初始化两次。所以上述的编码方式时线程不安全的。

二、双重加锁

class Singleton {  
 private:
      static Singleton * instance;  
      Singleton (){}    
      Singleton(const Singleton& other);
 public:
      static Singleton* getInstance() {  
      if (instance == nullptr) {  
          Lock lock;
          if(instance == nullptr){
              instance = new Singleton();  
          }
      }  
          return instance;  
      }
}  

上述代码是双重锁机制,当一个线程先获取到锁的时候,第二个线程读锁时就会等待第一个锁初始化完对象后才继续执行。但是上述写法没有考虑到内存读写reorder不安全。

正常创建对象实例会走三步骤

1) 分配对象内存

2) 调用构造器,执行初始化

3) 将对象的引用赋值给变量。

然而在编译器优化等问题下,在实际可能的运行顺序是,先执行第三步再执行第二部,即先引用给对象再调用构造器。

如果

假如其中一个线程在执行   instance = new Singleton();   语句时,分配完了内存,将对象将对象的引用赋值给变量,此时第二个线程判断 if (instance == nullptr) 不成立直接返回了一个还未初始化完的对象。那么就会造成线程不安全。这个创建对象的二三步骤乱序执行实际上叫重排序。

在JAVA ,C#中 ,新增了一个关键字volatile,在声明成员变量时  volatile static Singleton instance;    表示禁止 instance创建对象时重排序。

而在C++ 11版本的跨平台实现

//C++ 11版本之后的跨平台实现 
// atomic c++11中提供的原子操作
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

/*
* std::atomic_thread_fence(std::memory_order_acquire); 
* std::atomic_thread_fence(std::memory_order_release);
* 这两句话可以保证他们之间的语句不会发生乱序执行。
*/
Singleton* Singleton::getInstance() {
    Singleton* 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> lock(m_mutex);
        tmp = m_instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton;
            std::atomic_thread_fence(std::memory_order_release);//释放内存fence
            m_instance.store(tmp, std::memory_order_relaxed);
        }
    }
    return tmp;
}

或者有更简洁的C++写法,但在C++11版本前不支持。

class Singleton{
public:
    // 注意返回的是引用。
    static Singleton& getInstance(){
        static Singleton m_instance;  //局部静态变量
        return m_instance;
    }
private:
    Singleton(); //私有构造函数,不允许使用者自己生成对象
    Singleton(const Singleton& other);
};

猜你喜欢

转载自blog.csdn.net/superSmart_Dong/article/details/114604172
今日推荐