首页 > 代码库 > c++之单例模式
c++之单例模式
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_; } #endif
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++之单例模式