標籤:效率 編譯器最佳化 volatile phoenix 線程調度 .com 替換 memory 沒有
【單件類】保證只能有一個執行個體化對象,並提供全域的訪問入口。【設計注意事項】1.阻止所有執行個體化的方法:private 修飾建構函式,賦值建構函式,賦值拷貝函數。2.定義單一實例化對象的方法:a.使用static 修飾b.使用new+delete的方法3.多線程版本:使用雙檢測鎖定,即先檢測單一實例對象是否存在;不存在,使能“鎖”,再次判斷執行個體是否存在,不存在就建立該單一實例對象。A.單層鎖樣本:
- Singleton* Singleton::getInstance() {
- Lock lock; // scope-based lock, released automatically when the function returns
- if (m_instance == NULL) {
- m_instance = new Singleton;
- }
- return m_instance;
- }
B.DCL樣本:【單層鎖存在高並發時效率低,DCL提出先檢測單件指標m_instance是否已建立,減少大部分的鎖;上鎖後,再次檢查m_instance 】
- Singleton* Singleton::getInstance() {
- if(m_instance==NULL)
- {
- Lock lock; // scope- based lock, released automatically when the function returns
- if (m_instance == NULL) {
- m_instance = new Singleton;
- }
- }
-
- return m_instance;
- }
【DCL的風險】回顧下(或者學習下) m_instance = new Singleton; 發生了什麼:1.分配Singleton對象所需的記憶體2.為該記憶體地區執行建構函式3.m_instance指向該記憶體。一切都似乎沒有什麼問題,但是有時編譯器喜歡把2和3替換下(先不管編譯器出於什麼目的)執行單例化構造( m_instance = new Singleton; )的順序中,其他線程訪問對象程式未加鎖【lock一般不阻止CPU線程發送器,只對倆個線程裡同樣上了同個鎖的部分函數有阻塞作用】,直接存取會出故障【操作未定義的對象】,又不能所有地方都加鎖--效率低。解決方案:在單例化構造中先構造給臨時變數,再把臨時變數賦值給單例化對象的指標,注意防止編譯器最佳化,否則前功盡棄【當然,這種方式的弊端目前尚未考慮到】。
- Singleton* Singleton::getInstance() {
- volatile Singleton* tmp = m_instance;
- ... // insert memory barrier
- if (tmp == NULL) {
- Lock lock;
- tmp = m_instance;
- if (tmp == NULL) {
- tmp = new Singleton;
- ... // insert memory barrier
- m_instance = tmp;
- }
- }
- return tmp;
- }
【背景知識】2000年,一個JAVA高效能研究小組發布了一篇聲明《雙重檢查鎖定可能導致鎖定無效》。2004年,Scott Meyers 和Andrei Alexandrescu聯合發表了一篇名為《C++實現雙重檢查鎖定存在嚴重缺陷》【參考連結】http://blog.csdn.net/nodeathphoenix/article/details/51657973http://developer.51cto.com/art/201311/419604.htm
【C++設計模式】單件類與DCLP(Double Check Lock Pattern)的風險