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

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

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

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

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

[cpp]  view plain copy
  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以前,仍需要加锁。

[cpp]  view plain copy
  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?因为静态实例初始化在程序开始时进入主函数之前就由主线程以单线程方式完成了初始化,不必担心多线程问题。

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

[cpp]  view plain copy
  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意为垃圾工人):

[cpp]  view plain copy
  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的析构函数,该析构函数会删除单例的唯一实例。

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

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

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

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

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

具体代码如下:

[cpp]  view plain copy
  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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值