單例模式——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

發佈了12 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章