VC++ 2017的一个单例模板类

发布者:blowfish
发布于:2017-11-09 20:29
一百个人有一百种单例的写法,C++中轮子总是被重复造。

我目前的需求是:
1、用于VC2017,EXE/DLL都能用。
2、单例。当然,这隐含意思是其构造得是线程安全的。
3、只初始化一次。在构造之外进行的额外初始化操作,只执行一次,无论成功还是失败。
4、能析构。如果leak,不释放资源,怎么都觉得不够优雅。需要注意的是,如果在DLL中,单例的析构是在DLL_PROCESS_DETACH阶段执行的,此时是持有ntdll的loader锁的,很多操作都容易导致卡死。所以不要把复杂的操作放在析构阶段。
5、自己实现线程安全的构造、初始化。modern c++的static对象的构造默认就是线程安全的,为什么还要多此一举呢? 是因为微软的DLL loader在XP/2003上有(大)bug:如果用LoadLibrary()去load一个DLL时,DLL loader居然没初始化这个DLL的线程局部存储,而这个DLL恰好是用/Zc:threadSafeInit+编译的话,编译器生成的代码会一上来就直接引用并未被初始化的线程局部存储,导致崩掉。 为了能支持 XP/2003,就只能自己实现static对象的线程安全构造了。为此要增加编译命令行参数/Zc:threadSafeInit-来禁用编译器自己的线程安全构造。
6、方便继承。尽可能不用开销较大的虚函数。用模板。

关于单例的实现,最经典的应该是Scott Meyers和Andrei Alexandrescu写的《C++ and the Perils of Double-Checked Locking》(http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf)。DCLP(Double-Checked Locking Pattern)能保证多线程下的高效率。为了更好地支持用DCLP,VC++甚至增加了/volatile:ms的编译器私家扩展,使得volatile关键字默认就具有acquire/release语义。但是有了modern c++的memory model,就可以用/volatile:iso关掉这个特性,只依赖于 modern c++的memory model来实现DCLP。

下面是一个单例模板类的实现。使用方法是:
1、在stdafx.h中包含如下头文件:
#include <mutex>
#include <atomic>
2、 从本模板类派生出子类。
3、 在子类中实现public函数InitOnce()。

例子:
class CSingletonExample: public CSingletonT<CSingletonExample>
{
	public:
		CSingletonExample() {}
		~CSingletonExample() {}

		bool InitOnce(void)
		{
			return true;
		}

	private:
		friend class CSingletonT<CSingletonExample>;
};

CSingletonExample::Instance().Init();

单例模板类:

template<typename T>
class CSingletonT
{
	public:

		static T *InstancePtr()
		{
			T * pInstance = s_Instance.load(std::memory_order_acquire);
			if (pInstance)
				return pInstance;

			std::lock_guard<std::mutex> CreateLock(s_Mutex);
			pInstance = s_Instance.load(std::memory_order_relaxed);
			if (pInstance)
				return pInstance;

			static T staticObj;

			pInstance = &staticObj;
			s_Instance.store(pInstance, std::memory_order_release);

			return pInstance;
		}

		inline static T & Instance()
		{
			return *InstancePtr();
		}

		CSingletonT() 
			:m_InitState(SINGLETON_INIT_UNKNOWN)
		{
		}

		~CSingletonT()
		{
			#ifdef _DEBUG
				s_Instance.store(NULL, std::memory_order_relaxed);
				m_InitState.store(SINGLETON_INIT_UNKNOWN, std::memory_order_relaxed);
			#endif
		}

		/*
			此处所调用到的public函数InitOnce()需要子类自己实现,原型是:
				bool InitOnce(void);
		*/
		bool Init(void)
		{
			SINGLETON_INIT_STATE InitState = m_InitState.load(std::memory_order_acquire);
			if (InitState != SINGLETON_INIT_UNKNOWN)
				return InitState == SINGLETON_INIT_SUCCESS;

			std::lock_guard<std::mutex> InitLock(s_Mutex);
			InitState = m_InitState.load(std::memory_order_relaxed);
			if (InitState != SINGLETON_INIT_UNKNOWN)
				return InitState == SINGLETON_INIT_SUCCESS;

			InitState = (static_cast<T *>(this)->InitOnce() ? SINGLETON_INIT_SUCCESS : SINGLETON_INIT_FAILED);
			m_InitState.store(InitState, std::memory_order_release);

			return InitState == SINGLETON_INIT_SUCCESS;
		}

		CSingletonT(const CSingletonT&) = delete;
		CSingletonT& operator=(const CSingletonT&) = delete;

	private:

		//单例的初始化状态,为三态。
		enum SINGLETON_INIT_STATE
		{
			SINGLETON_INIT_UNKNOWN,
			SINGLETON_INIT_SUCCESS,
			SINGLETON_INIT_FAILED,
		};

		std::atomic<SINGLETON_INIT_STATE> m_InitState;

		static std::atomic<T *> s_Instance;
		static std::mutex       s_Mutex;
};

template<typename T>
__declspec(selectany) std::atomic<T *> CSingletonT<T>::s_Instance;

template<typename T>
__declspec(selectany) std::mutex  CSingletonT<T>::s_Mutex;



声明:该文观点仅代表作者本人,转载请注明来自看雪