前言
前段时间在网上看到了个的面试题,大概意思是如何在不使用锁和C++11的情况下,用C++实现线程安全的Singleton。
看到这个题目后,第一个想法就是用Scott Meyer在《Effective C++》中提到的,在static成员函数中构造local static变量的方法来实现,但是经过一番查找、思考,才明白这种实现在某些情况下是有问题的。本文主要将从最基本的单线程中的Singleton开始,慢慢讲述多线程与Singleton的那些事。
单线程
在单线程下,下面这个是常见的写法:
template<typename T> class Singleton { public: static T& getInstance() { if (!value_) { value_ = new T(); } return *value_; } private: Singleton(); ~Singleton(); static T* value_; }; template<typename T> T* Singleton<T>::value_ = NULL; |
在单线程中,这样的写法是可以正确使用的,但是在多线程中就不行了。
多线程加锁
在多线程的环境中,上面单线程的写法就会产生race condition从而产生多次初始化的情况。要想在多线程下工作,最容易想到的就是用锁来保护shared variable了。下面是伪代码:
template<typename T> class Singleton { public: static T& getInstance() { { MutexGuard guard(mutex_) // RAII if (!value_) { value_ = new T(); } } return *value_; } private: Singleton(); ~Singleton(); static T* value_; static Mutex mutex_; }; template<typename T> T* Singleton<T>::value_ = NULL; template<typename T> Mutex Singleton<T>::mutex_; |
这样在多线程下就能正常工作了。这时候,可能有人会站出来说这种做法每次调用getInstance的时候都会进入临界区,在频繁调用getInstance的时候会比较影响性能。这个时候,为了解决这个问题,DCL写法就被聪明的先驱者发明了。