c++之單例模式
阿新 • • 發佈:2017-05-21
lsi 但是 desc 模式 單例模式 ron spl 希望 構造函數
1 本篇主要討論下多線程下的單例模式實現:
首先是 double check 實現方式: 這種模式可以滿足多線程環境下,只產生一個實例。
template<typename T> class dclsingleton { public: static T& GetInstance() { if(NULL == value_) { MutexGuard mg(mutex_);if (NULL == value_) { value_ = new T; } } return *value_; } protected: dclsingleton() { std::cout << "dclsingleton constuctor called" << std::endl; } dclsingleton(const dclsingleton &dcl) {} private: static T* value_; static Mutex mutex_; };
但是這種實現存在除bug的隱患, 問題就在: value_ = new T; 上。《程序員的自我修養》上指出:
這樣的代碼是有問題的,問題的來源在於 cpu 的亂序執行。c++裏的new 包含了兩步。
(1)分配內存
(2)調用構造函數
所以 value_ = new T; 實際上包含了三步:
(1)分配內存
(2)在分配內存的位置上調用構造函數
(3)將內存地址賦值給 value_;
這三步中,(2), (3)兩步是可以顛倒的,也就是說,可能出現,先執行(3)這是 value_已經不為NULL, 當出現另一個對GetInstance的並發調用,if 內的 value_ != NULL於是返回,但是還沒有調用構造函數。於是使用這個指針的時候,就會導致崩潰。
這時候需要保證(2), (3)的執行順序,通常需要加上內存屏障,保證一定保證(2)執行完以後,再執行(3)
這裏我加上了__sync_synchronize(); 後 實現是這樣的:
static T& GetInstance() { if(NULL == value_) { MutexGuard mg(mutex_); if (NULL == value_) { T* tmp = static_cast<T*>(operator new (sizeof(T))); new (tmp) T(); __sync_synchronize(); value_ = tmp; } } return *value_; }
這樣便可以既保證多線程環境安全,又保證不會出現上面的問題。
2. 加上內存屏障的示例代碼:dcl_single.h
#ifndef __DCL_SINGLE_H #define __DCL_SINGLE_H #include <iostream> namespace yl { class Mutex { public: Mutex() { pthread_mutex_init(&mutex_, NULL); } ~Mutex() { pthread_mutex_destroy(&mutex_); } public: void Lock() { pthread_mutex_lock(&mutex_); } void UnLock() { pthread_mutex_unlock(&mutex_); } private: pthread_mutex_t mutex_; }; class MutexGuard { public: MutexGuard(Mutex& m) : mutex_(m) { mutex_.Lock(); } ~MutexGuard() { mutex_.UnLock(); } private: Mutex mutex_; }; template<typename T> class dclsingleton { public: static T& GetInstance() { if(NULL == value_) { MutexGuard mg(mutex_); if (NULL == value_) { T* tmp = static_cast<T*>(operator new (sizeof(T))); new (tmp) T(); __sync_synchronize(); value_ = tmp; } } return *value_; } protected: dclsingleton() { std::cout << "dclsingleton constuctor called" << std::endl; } dclsingleton(const dclsingleton &dcl) {} private: static T* value_; static Mutex mutex_; }; template<typename T> T* dclsingleton<T>::value_ = NULL; template<typename T> Mutex dclsingleton<T>::mutex_; } #endifView Code
singletonTest.cpp
#include "dcl_single.h" #include <iostream> namespace yl { class MgrSg : public dclsingleton<MgrSg> { private: friend class dclsingleton <MgrSg>; MgrSg(){ std::cout << "MgrSg: constructor called" << std::endl; } ~MgrSg() { std::cout << "MgrSg: desconstructor called" << std::endl; } public: void print() { std::cout << "print called" << std::endl; } }; } int main(void) { using namespace yl; MgrSg::GetInstance().print(); return 0; }
3. 還可以用 unix 下的 pthread_once 來實現單例模式:
template <typename T> class MySingleton { public: static T & getInstance() { pthread_once(&ponce_, &MySingleton::init); return *instance; } protected: MySingleton() {} MySingleton(const MySingleton&) {} private: static void init() { instance = new T(); } private: static pthread_once_t ponce_; static T *instance; }; template<typename T> pthread_once_t MySingleton<T>::ponce_ = PTHREAD_ONCE_INIT; template<typename T> T *MySingleton<T>::instance = nullptr; }
4.我自己遇到的就是以上兩種情況,若是希望了解更全面,可參考下邊:
http://www.cnblogs.com/liyuan989/p/4264889.html
5. 水平有限,望及時指出錯誤。謝謝
c++之單例模式