在相似「多線程中使用單例的懶漢式初始化」場景中,爲了提升效率,一般不是簡單的鎖定,這會致使沒必要要的線程序列化。許多人都試圖想出一個更好的實現方法,包括臭名昭著的雙重檢查鎖定(Double-Checked Locking)模式(DCLP)。ios
#include <iostream> #include <thread> #include <mutex> class Singleton { public: static Singleton* getInstance() { if (!instancePtr) { std::lock_guard<std::mutex> guard(initMutex); if (!instancePtr) { instancePtr = new Singleton(); std::cout << "init" << std::endl; } } return instancePtr; } private: Singleton() = default; Singleton(const Singleton&) = default; Singleton& operator=(const Singleton&) = default; //garbo類用於自動釋放單例實例(棧變量在析構函數中析構instance) class Garbo { public: ~Garbo() { if (Singleton::instancePtr) { delete Singleton::instancePtr; } } }; private: static Singleton* instancePtr; static std::mutex initMutex; static Garbo delGarbo; }; Singleton* Singleton::instancePtr = nullptr; std::mutex Singleton::initMutex; Singleton::Garbo Singleton::delGarbo = Singleton::Garbo(); int main() { { Singleton* inst_1 = Singleton::getInstance(); std::thread t2([=]() { Singleton* inst_2 = Singleton::getInstance(); }); std::thread t3([=]() { Singleton* inst_3 = Singleton::getInstance(); }); t2.join(); t3.join(); } system("pause"); return 0; }
雙重檢測鎖定有一個在代碼層面看不到的問題,當執行 instance=new Singleton; 時,實際上分爲三個步驟:多線程
- 第一步:爲Singleton對象分配一片內存
- 第二步:構造一個Singleton對象,存入已分配的內存區
- 第三步:將instance指向這片內存區
實際上,編譯器有時會交換步驟2和步驟3的執行順序。可是本文並不打算複製粘貼這一部份內容(太長了),感興趣的能夠參考:併發
在C++11中能夠使用靜態初始化器完成單例初始化的操做,C++11標準規定,若是控制進入申明同時變量將被初始化的時候,那麼併發執行將會等到初始化的完成。函數
Singleton& getInstance() { static Singleton instance; return instance; }
此外,C++11還提供了std::call_once準確執行一次可調用對象,即便同時從多個線程調用。spa
template< class Callable, class... Args >
void call_once( std::once_flag& flag, Callable&& f, Args&&... args );.net
若在調用 call_once 的時刻,若 flag 指示已經調用了 f ,則 call_once 當即返回。藉助 call_once 修改下上面的單例:線程
class Singleton { public: static Singleton* getInstance() { std::call_once(initFlag, []() { instancePtr = new Singleton(); std::cout << "init" << std::endl; }); return instancePtr; } private: Singleton() = default; private: static Singleton* instancePtr; static std::once_flag initFlag; }; Singleton* Singleton::instancePtr = nullptr; std::once_flag Singleton::initFlag;