我之前寫過單例的文章( http://www.cnblogs.com/mkdym/p/4908644.html ),但是現在又有了一些想法,不想再在原來那篇文章上更新,所以獨開一篇。
看到一些書或者文章講單例模式的時候,總要說起DCLP,並且得出的結論是DCLP是不可靠的,也就不能用它來實現單例。即下面這樣做是錯誤的:
class SingletonAA { public: static SingletonAA& get_instance_ref() { if (NULL == p_) // 1st check { scoped_lock lock; if (NULL == p_) // 2nd check { p_ = new SingletonAA(); } } return *p_; } private: SingletonAA() { //... } ~SingletonAA() { //... } private: static SingletonAA *p_; }; SingletonAA *SingletonAA::p_ = NULL;
因為new那一句不是原子的,分了3步,而且順序不一定,這個順序又會影響第一次檢查的結果。接著他們又討論了volatile關鍵字和亂序優化,然後得出結論這個關鍵字也不能使DCLP變得正確,或者說不能輕松的使DCLP變得正確。
但是他們講這個問題的時候全部的前提是用單例對象的指針本身去做了判斷條件,這就是誘因:單例對象的創建和單例的判斷是對同一個元素讀寫,而“寫”太“復雜”了!
那麼我不用單例對象去做判斷不就行了嗎?如下:
class SingletonAA { public: static SingletonAA& get_instance_ref() { if (!init_flag_) // 1st check { scoped_lock lock; if (!init_flag_) // 2nd check { p_ = new SingletonAA(); init_flag_ = true; } } return *p_; } private: //... private: static SingletonAA *p_; static bool volatile init_flag_; }; SingletonAA *SingletonAA::p_ = NULL; bool volatile SingletonAA::init_flag_ = false;
我換用一個bool標志,bool變量在vc上是一個字節的,操作只需一條指令,是原子的。而且聲明成volatile,確保編譯器不對它做優化。
按我的不准確的知識,編譯器或CPU可能會對
p_ = new SingletonAA();
和
init_flag_ = true;
這兩句調整順序,因為他們兩句沒有關聯,這就又會出錯。那麼我制造一個關聯:
init_flag_ = p_ ? true : false;
使init_flag_的賦值依賴於對象的創建,按我的理解,此時編譯器和CPU都應該使設置標志語句後於對象創建語句,那麼就沒有錯誤了。
假如編譯器認為p_在執行完new後一定不為0,那麼它就又可以優化init_flag_的賦值了。那麼這種情況會不會出現,編譯器是否會這樣認為?
Need Help
Need Help
Need Help
(重說三)
因為我想得到大家的關注,所以可恥的勾選了“發布至博客園首頁”,如果最後沒有在首頁出現,我就把這句話刪掉,免的丟我的小臉。