单例模式-看这篇就够了!!!

Posted 小龙养基场

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式-看这篇就够了!!!相关的知识,希望对你有一定的参考价值。



单列模式,顾名思义,一个类只有一个实例。所以单列模式的特征为:

  1. 只有一个实例

  2. 必须提供一个全局访问点(静态成员方法或静态静态成员变量)

  3. 不可以复制拷贝

根据以上描述的特征,那么一个简单的单例模式就诞生了,如下代码所示

template <typename T>class Singleton{public: static T* Instance(){ if (m_pInstance == nullptr) m_pInstance = new T(); return m_pInstance; } static void DestroyInstance(){ if (m_pInstance != nullptr) { delete m_pInstance; m_pInstance = nullptr; } }private: Singleton(){} ~Singlento(){}  Singleton(const Singleton&); Singleto& operator = (const Singleton&); private: static T* m_pInstance;};
template <typename T>T* Singleton<T>::m_pInstance = nullptr;


我们分析下上面代码,如果在单线程环境下面运行,没有什么问题,假设在多线程环境中,两个线程同时运行到m_pInstance == nullptr,此时条件为真,那么就会创建两个实例,这不符合单列的特征,那么在这里需要改进,在改进前,我们先用c++11实现一个不可复制的类Noncopyable,以后让其继承该类即可


class Noncopyable{protected: Noncopyable() = default;  ~Noncopyable() = default; Noncopyable(const Noncopyable&) = delete;  Noncopyable& operator = (const Noncopyable&) = delete;};


然后改进后的单列类如下


#include <mutex>template <typename T>class Singleton : Noncopyable{public: static T* Instance(){ std::lock_guard<std::mutex> lock(m_mutex); if (m_pInstance == nullptr) { m_pInstance = new T(); } return m_pInstance; } static void DestroyInstance(){ std::lock_guard<std::mutex> lock(m_mutex); if (m_pInstance != nullptr) { delete m_pInstance; m_pInstance = nullptr; } }private: static T* m_pInstance; static std::mutex m_mutex;};template <typename T>T* Singleton<T>::m_pInstance = nullptr;
template <typename T>std::mutex Singleton<T>::m_mutex;


我们分析下上面代码,解决线程安全问题无非就是加锁,但是如果线程很多情况下,每个线程都要等拿到锁的线程运行结束后才继续执行,这样无疑会导致大量的线程阻塞,那么该如何解决呢,解决办法就是在锁之前先判断是否为nullptr,于是改进后的代码如下:


#include <mutex>template <typename T>class Singleton : Noncopyable{public: static T* Instance(){ if (m_pInstance == nullptr) { std::lock_guard<std::mutex> lock(m_mutex); if (m_pInstance == nullptr) { m_pInstance = new T(); } } return m_pInstance; } static void DestroyInstance(){ if (m_pInstance != nullptr) { std::lock_guard<std::mutex> lock(m_mutex); if (m_pInstance != nullptr) { delete m_pInstance; m_pInstance = nullptr; } } }private: static T* m_pInstance; static std::mutex m_mutex;};template <typename T>T* Singleton<T>::m_pInstance = nullptr;template <typename T>std::mutex Singleton<T>::m_mutex;


继续分析上面的代码,其实这就是所谓的双检锁机制。但是请注意,如果数据量很大的情况,加锁释放锁本来就是耗时的操作,所以在大数据情况下,这种双检锁机制的单列模式性能就显得堪忧了,所以我们应该避免加锁操作,于是就出现了另外一种单列模式


template <typename T>class Singleton : Noncpyable{public: static T* Instance(){ return m_pInstance; }private: static T* m_pInstance;};template <typename T>T* Singleton<T>::m_pInstance = new T();
template <typename T>class Singleton : Noncopyable{public: static T* Instance(){ return &m_Instance; }private: static T m_Instance;};template <typename T>T Singleton<T>::m_Instance;


这样的实现应该算是比较好了,但是还有比这更好的完美方法,利用linux的pthread_once和c++11的std::call_once


template <typename T>class Singleton : Noncopyable{public: static T* Instance(){ std::call_once(m_flag,&Singleton::Init); //c++11 //pthread_once(&m_ponce,&Singleton::Init); //linux return m_pInstance;  } static void DestroyInstance(){ delete m_pInstance; m_pInstance = nullptr;  }private: static T* m_pInstance; static std::once_flag m_flag; //static pthread_once_t m_ponce; static void Init(){ m_pInstance = new T(); }};//template <typename T>//pthread_once_t Singleton<T>::m_ponce = PTHREAD_ONCE_INIT;template <typename T>T* Singleton<T>::m_pInstance = nullptr;


另外放一个超级大招,利用c++11的可变模板参数,放一个万能的单列模式


template <typename T>class Singleton : Noncopyable{public: template <typename... Args> static T* Instance(Args... args){ std::call_once(m_flag,&Singleton::Init,args); return m_pInstance; } static void DestroyInstance(){ delete m_pInstance; m_pInstance = nullptr; }private: static T* m_pInstance;  static std::once_flag = m_flag; template <typename... Args> static void Init(Args&&.. args){ return new T(std::forward<Args>(args)...); }};
template <typename T>T* Singleton<T>::m_pInstance = nullptr;template <typename T>std::once_flag Singleton<T>::m_flag;


好了,单列模式就分析到这里了,最后一种是万金油。

上一个测试代码吧


#include <iostream>#include "Singleton4.hpp"struct A{ A() { std::cout << "A is constrcut!" << std::endl; num = 0; } ~A() { std::cout << "A is desconstruct!" << std::endl;  }
int num; void add(int n) { num = num + n; } int getNum() { return num; }};
struct B{ int a_; explicit B(int a) : a_(a)  { } int get() { return a_; }};int main(){ A *a = Singleton<A>::Instance();  A *b = Singleton<A>::Instance(); std::cout << "a is" << a << std::endl;  std::cout << "b is" << b << std::endl; a->add(5);  std::cout << "b.num=" << b->getNum() << std::endl; b->add(10); std::cout << "a.num=" << a->getNum() << std::endl;  std::cout << "b.num=" << b->getNum() << std::endl;  B *bb = Singleton<B>::Instance(5); std::cout << bb << std::endl;  std::cout << "bb.a=" << bb->get() << std::endl;}

由于本人水平有限,若有错误,欢迎指出,谢谢!


以上是关于单例模式-看这篇就够了!!!的主要内容,如果未能解决你的问题,请参考以下文章

学Dapr Actors 看这篇就够了

如何优雅的用策略模式,取代臃肿的 if-else 嵌套,看这篇就够了

对于php-fpm 三种运行模式,看这篇就够了

对于php-fpm 三种运行模式,看这篇就够了

Handler原理剖析,看这篇就够了

Handler原理剖析,看这篇就够了