在网上看到一篇关于将线程安全的单例实现,http://www.haogongju.net/art/1688900。下面是他提到的出自google的源码:
template <typename Ty_> class LazySingleton { public: static Ty_& GetInstance(){ 1: while(me_ == NULL || me_ == (void*)-1){ 2: PVOID result = InterlockedCompareExchangePointer((PVOID*)&me_, -1, NULL); 3: if(*(PVOID*)&me_ == -1){ 4: Ty_* new_instance = new Ty_(); 5: InterlockedCompareExchangePointer((PVOID*)&me_, (PVOID)new_instance, -1); } } 6: return *const_cast<Ty_*>(me_); } private: static volatile Ty_ *me_; }; template <typename Ty_> volatile Ty_* LazySingleton<Ty_>::me_;
PVOID g_pwidCached = NULL;
class Singlton { public: ~Singlton(){}; static Singlton* GetInstance() { Singlton *pwid = (Singlton*)g_pwidCached; if (!pwid) { pwid = new(nothrow) Singlton(); if (pwid) { Singlton* pwidOld = reinterpret_cast<T*>(InterlockedCompareExchangePointerRelease(&reinterpret_cast<PVOID&>(g_pwidCached), pwid, NULL)); if (pwidOld) { delete pwid; pwid = pwidOld; } } } return pwid; } };解释:定义一个全局变量g_pwidCached。线程中调用GetInstance()时首选获取g_pwidCached,并赋值给局部变量pwid,如果pwid不为空,则单例已经创建,直接返回。如果pwid为空,说明单例尚未创建,调用pwid = new Singlton()创建一个实例,注意,这里如果多线程并发调用,是有可能创建多个Singlton的实例的。
Singlton* pwidOld = reinterpret_cast<T*>(InterlockedCompareExchangePointerRelease(&reinterpret_cast<PVOID&>(g_pwidCached), pwid, NULL));这里调用了原子操作函数IngerlockedCompareExchangePointerRelease,判断g_pwidCached是否为空,如果不为空,则执行g_pwidCached = pwid,并返回g_pwidCached原先的值给pwidOld。由于是原子操作,当多线程new出多个Singlton实例时,第一个执行 IngerlockedCompareExchangePointerRelease的线程会将它new出的Singlton实例赋给g_pwidCached,并返回null给pwidOld,其他线程阻塞。当其他线程解除阻塞执行到IngerlockedCompareExchangePointerRelease时,由于g_pwidCached已经不为null,则不执行g_pwidCached = pwid,并返回非null给pwidOld。这样就保证了多线程同时第一次调用GetInstance()时只有一个实例被赋给g_pwidCached,并且函数GetInstance()返回正确的pwid。函数中的
if (pwidOld) { delete pwid; pwid = pwidOld; }保证了多个线程new出的多个Singlton实例,除了被用上的那个实例外的其他实例都被delete掉。避免泄露。