程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Singleton之C++部分一

Singleton之C++部分一

編輯:C++入門知識

采用靜態或者全局變量的實現方案

由於C++不能保證靜態或者全局對象的構造函數的調用順序以及析構順序。所以如果程序中有多個用此方法實現的Singleton類,它們之間又有某種構造依賴關系和析構依賴關系,就會造成災難性的後果。所以,只有當肯定不會有構造和析構依賴關系的情況下,這種實現才是合適的。

> 優點 實現簡單,多線程下安全 > 缺點 如果有多個Singleton對象的創建順序有依賴時,千萬別用;不是lazy loading,有些浪費。

Meyers Singleton來控制構造順序,但是不能控制析構順序

Scott Meyer在<<Effective C++>>3rd Item4中提出了一個解決方案,當將non-local static變量移動到靜態方法中成為local static變量的時候。C++保證當第一次靜態方法被調用的時候,才會創建該靜態變量。但是這裡有一個疑問,創建順序能夠被控制了,可是析構順序呢?我們只知道進程結束的時候,local static 變量會被析構,而且按照創建順序的相反順序進行。如果幾個Singleton類的析構函數之間也有依賴關系,並且這種依賴順序關系和LIFO順序沖突,就會造成dead-reference問題。

> 優點 實現簡單;用的時候才創建,比較節省。 > 缺點 多線程下不安全;如果有多個Singleton對象的析構順序有依賴時,要小心

DCLP 98標准下是不可靠的,0x標准下是可靠的

DCLP 就是 Double-checked locking pattern.用於在多線程環境下保證只創建Singleton對象。第一次check不用加鎖,但是第二次check和創建對象必須加鎖。還要注意編譯器可能會優化代碼,導致DCLP模式失效。因此要使用volatile 修飾T* pInstance變量。先看一下 DCLP的實現代碼:

 
> class Singleton {
> public:
>     static Singleton* instance() {
>         if (pInstance == 0) {
>             Lock lock;
>             if (pInstance == 0) {
>                 pInstance = new Singleton;
>             }
>         }
>         return pInstance;
>     }
> private:
>     static Singleton * volatile pInstance;
>     Singleton(){
>     }
> };
> 


在c++98標准下,這是不可靠的。原因有三點:
一,執行順序得不到保證。編譯器會優化代碼,從而改變執行順序。
        pInstance = new Singleton; 這個語句會分成三步完成:1.分配內存,2.在已經分配的內存上調用構造函數創建對象,3.將對象賦值給指針pInstance.但是這個順序很可能會被改變為1,3,2。如果A線程在1,3執行完後,B線程執行第一個條件判斷if(pInstance ==0),此時鎖不能起到保護作用。B線程會認為pInstance已經指向有效對象,可以去使用了。嘿嘿,災難發生。主要原因是C++98標准中沒有包含多線程,只假定是單線程,編譯器的優化行為無視多線程環境,因此產生的優化代碼可能會被去掉或者改變順序。我們沒有辦法在98標准的采用標准c++語言來解決這個問題,只能采用平台相關的多線程API和與之兼容的編譯器來解決。因此,從本質上來說,基於98標准,此問題無解。
二,volatile對於執行順序也沒有幫助。
三,多處理器的系統,B處理器看到變量值的順序可能和A處理器寫變量值的順序不一致。

詳細解釋請參考Scott Meyers and Andrei Alexandrescu的論文:http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
技術總是發展的,2011標准的出台給DCLP帶來了曙光,真的還能用麼,拭目以待吧.

讓我們先下個結論:

> 缺點 98標准下不可用 > 優點 讓我們接受教育,包括Andrei和Scott meyer都犯過錯。



Andrei說4.11 scott meyer闡述了0x標准下,由於有了線程概念,內存模型,sequence point被sequenced before 和 happens before取代, 有了atomic等等,DCLP又可以復活了。
http://cppandbeyond.com/2011/04/11/session-announcement-the-c0x-memory-model-and-why-you-care/
具體談了什麼呢?我還沒有找到相關的文檔。

簡單鎖

於是又回到老土的鎖方案,其實注意一下調用,還是能夠提高效率的。

 
> class Singleton {
> public:
>     static Singleton* instance() {
>         Lock lock;
>         if (pInstance == 0) {
>             pInstance = new Singleton;
>         }
>         return pInstance;
>     }
> private:
>     static Singleton * volatile pInstance;
>     Singleton(){
>     }
> };
> 

客戶調用時,在每個線程的開頭都獲得Singleton* p = Singleton::instance();以後就一直使用這個p變量,應該說還是能有效的降低同步的機會。避免頻繁調用Singleton::instance()->就好。

> 優點 實現簡單,線程安全 > 缺點 客戶需要意識到,並且遵守少調用的原則。

 

程序開始之前創建Single

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved