• 单例模式——C++实现线程安全的单例


    一、懒汉模式:即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例。

    需要用锁,来保证其线程安全性:原因:多个线程可能进入判断是否已经存在实例的if语句,从而non thread safety.

    使用double-check来保证thread safety.但是如果处理大量数据时,该锁才成为严重的性能瓶颈。

    1、静态成员实例的懒汉模式:

    1. <pre name="code" class="cpp">class Singleton  
    2.   {  
    3.   private:  
    4.       static Singleton* m_instance;  
    5.       Singleton(){}  
    6.   public:  
    7.       static Singleton* getInstance();  
    8.   };  
    9.     
    10.  Singleton* Singleton::getInstance()  
    11.  {  
    12.      if(NULL == m_instance)  
    13.      {  
    14.          Lock();//借用其它类来实现,如boost  
    15.          if(NULL == m_instance)  
    16.          {  
    17.              m_instance = new Singleton;  
    18.          }  
    19.          UnLock();  
    20.      }  
    21.      return m_instance;  
    22.  }</pre>  

    2、内部静态实例的懒汉模式

    这里需要注意的是,C++0X以后,要求编译器保证内部静态变量的线程安全性,可以不加锁。但C++ 0X以前,仍需要加锁。

    1. class SingletonInside  
    2.  {  
    3.   private:  
    4.       SingletonInside(){}  
    5.   public:  
    6.       static SingletonInside* getInstance()  
    7.       {  
    8.           Lock(); // not needed after C++0x  
    9.           static SingletonInside instance;  
    10.          UnLock(); // not needed after C++0x  
    11.          return instance;   
    12.      }  
    13.  };  

    二、饿汉模式:即无论是否调用该类的实例,在程序开始时就会产生一个该类的实例,并在以后仅返回此实例。

    由静态初始化实例保证其线程安全性,WHY?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。

    故在性能需求较高时,应使用这种模式,避免频繁的锁争夺。

    1. class SingletonStatic  
    2.  {  
    3.  private:  
    4.      static const SingletonStatic* m_instance;  
    5.      SingletonStatic(){}  
    6.  public:  
    7.      static SingletonStatic* getInstance()  
    8.      {  
    9.          return m_instance;  
    10.     }  
    11. };  
    12.   
    13. //外部初始化 before invoke main  
    14. const SingletonStatic* SingletonStatic::m_instance = new SingletonStatic;  



    m_pInstance指向的空间什么时候释放呢?更严重的问题是,该实例的析构函数什么时候执行?

    如果在类的析构行为中有必须的操作,比如关闭文件,释放外部资源,那么上面的代码无法实现这个要求。我们需要一种方法,正常的删除该实例。

    可以在程序结束时调用GetInstance(),并对返回的指针掉用delete操作。这样做可以实现功能,但不仅很丑陋,而且容易出错。因为这样的附加代码很容易被忘记,而且也很难保证在delete之后,没有代码再调用GetInstance函数。

    一个妥善的方法是让这个类自己知道在合适的时候把自己删除,或者说把删除自己的操作挂在操作系统中的某个合适的点上,使其在恰当的时候被自动执行。

    我们知道,程序在结束的时候,系统会自动析构所有的全局变量。事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,而它的唯一工作就是在析构函数中删除单例类的实例。如下面的代码中的CGarbo类(Garbo意为垃圾工人):

    1. class CSingleton  
    2. {  
    3. //其他成员  
    4. public:  
    5. static CSingleton* GetInstance();  
    6. private:  
    7.     CSingleton(){};  
    8.     static CSingleton * m_pInstance;  
    9. class CGarbo //它的唯一工作就是在析构函数中删除CSingleton的实例  
    10. {  
    11.         public:  
    12.             ~CGarbo()  
    13.             {  
    14.                 if( CSingleton::m_pInstance )  
    15.                   delete CSingleton::m_pInstance;  
    16.            }  
    17.  }  
    18.         Static CGabor Garbo; //定义一个静态成员,程序结束时,系统会自动调用它的析构函数  
    19. };  

    类CGarbo被定义为CSingleton的私有内嵌类,以防该类被在其他地方滥用。

    程序运行结束时,系统会调用CSingleton的静态成员Garbo的析构函数,该析构函数会删除单例的唯一实例。

    使用这种方法释放单例对象有以下特征:

    在单例类内部定义专有的嵌套类;

    在单例类内定义私有的专门用于释放的静态成员;

    利用程序在结束时析构全局变量的特性,选择最终的释放时机;

    使用单例的代码不需要任何操作,不必关心对象的释放。

    具体代码如下:

    1. #include <iostream>>  
    2.   
    3. using namespace std;  
    4.   
    5. class Singleton  
    6.   
    7. {  
    8.   
    9. public:  
    10.   
    11.     static Singleton *GetInstance();  
    12.   
    13. private:  
    14.   
    15.     Singleton()  
    16.   
    17.     {  
    18.   
    19.         cout << "Singleton ctor" << endl;  
    20.   
    21.     }  
    22.   
    23.     ~Singleton()  
    24.   
    25.     {  
    26.   
    27.         cout << "Singleton dtor" << endl;  
    28.   
    29.     }  
    30.   
    31.     static Singleton *m_pInstance;  
    32.   
    33.     class Garbo  
    34.   
    35.     {  
    36.   
    37.     public:  
    38.   
    39.         ~Garbo()  
    40.   
    41.         {  
    42.   
    43.             if (Singleton::m_pInstance)  
    44.   
    45.             {  
    46.   
    47.                 cout << "Garbo dtor" << endl;  
    48.   
    49.                 delete Singleton::m_pInstance;  
    50.   
    51.             }  
    52.   
    53.         }  
    54.   
    55.     };  
    56.   
    57.     static Garbo garbo;  
    58.   
    59. };  
    60.   
    61. Singleton::Garbo Singleton::garbo;  // 一定要初始化,不然程序结束时不会析构garbo  
    62.   
    63. Singleton *Singleton::m_pInstance = NULL;  
    64.   
    65. Singleton *Singleton::GetInstance()  
    66.   
    67. {  
    68.   
    69.     if (m_pInstance == NULL)  
    70.   
    71.         m_pInstance = new Singleton;  
    72.   
    73.     return m_pInstance;  
    74.   
    75. }  
    76.   
    77. int main()  
    78.   
    79. {  
    80.   
    81.     Singleton *p1 = Singleton::GetInstance();  
    82.   
    83.     Singleton *p2 = Singleton::GetInstance();  
    84.   
    85.     if (p1 == p2)  
    86.   
    87.         cout << "p1 == p2" << endl;  
    88.   
    89.     return 0;  
    90.   
    91. }  


    输出结果如下:

    Singleton ctor

    p1 == p2

    Garbo dtor

    Singleton dtor

  • 相关阅读:
    Java反射机制详情
    Jsoup_Select 选择器
    JDK5.0新特性1
    Java注解类型(@Annotation)
    TSINGSEE青犀视频EasyDSS互联网直播/点播平台在出入口车牌识别中的应用
    如何通过视频智能检测平台+无人机对野生动植物进行检测?
    EasyNTS现场维护出现login to server failed:EOF问题调整
    无人机与推流直播平台助力道路交通安全管理
    EasyDSS直播视频卡顿在某一段循环播放问题排查
    EasyDSS如何通过Golang解析命令行参数实现版本号的显示?
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318552.html
Copyright © 2020-2023  润新知