最简单的singleton就是用一个全局对象或者函数内部的静态对象来实现。但是这样有两个明显的问题。(1) 全局对象是程序初始化的时候就构造的,如果程序启动要构造很多此类对象,会变得很昂贵。(2) C++标准并没有规定不同的编译单元的全局对象的构造顺序,所以如果不同Singleton之间的构造有相互依赖关系,那么行为是未知的。
- struct sgt1{
- static sgt1* GetInstance(){
- static sgt1 Inst;
- return &Inst;
- }
- };
- struct sgt2{
- static sgt2* pInst;
- static sgt2* GetInstance(){
- if(pInst==nullptr){
- pInst=new sgt2;
- }
- return pInst;
- }
- ~sgt2(){
- if(pInst)delete pInst;
- }
- };
struct sgt1{
static sgt1* GetInstance(){
static sgt1 Inst;
return &Inst;
}
};
struct sgt2{
static sgt2* pInst;
static sgt2* GetInstance(){
if(pInst==nullptr){
pInst=new sgt2;
}
return pInst;
}
~sgt2(){
if(pInst)delete pInst;
}
};
这个实现还有多线程的问题,如果两个线程同时访问pInst==nullptr这句话,那么同时得出pInst为空的结论,new执行了两次,显然不对。所以需要引入double checked lock机制。这个机制在C++98/03标准下可能有问题,因为编译器会打乱代码的实际执行顺序,因此需要引入操作系统相关的锁定操作,例如windows下面的mutex. C++0x/11标准则通过原子操作解决了这个问题。这样就是一个比较理想的Singleton类型了。
- HANDLE hEvent;
- struct sgt3{
- static sgt3* pInst;
- static sgt3* GetInstance(){
- if(pInst==nullptr){
- if(WaitForSingleObject(hEvent,INFINITE)!=WAIT_OBJECT_0){
- printf("Wait function failed: %d\n",GetLastError());
- return nullptr;
- }
- if(pInst==nullptr){
- pInst=new sgt3;
- }
- }
- SetEvent(hEvent);
- return pInst;
- }
- sgt3(){printf("%s\n",__FUNCTION__);}
- ~sgt3(){
- if(pInst)delete pInst;
- }
- };
- sgt3* sgt3::pInst=nullptr;
- int main(void){
- hEvent=CreateEvent(nullptr,TRUE,TRUE,_T("hello"));
- if(hEvent==INVALID_HANDLE_VALUE){
- printf("CreateEvent failed:%d\n",GetLastError());
- return 1;
- }
- sgt3::GetInstance();
- CloseHandle(hEvent);
- return 0;
- }
HANDLE hEvent;
struct sgt3{
static sgt3* pInst;
static sgt3* GetInstance(){
if(pInst==nullptr){
if(WaitForSingleObject(hEvent,INFINITE)!=WAIT_OBJECT_0){
printf("Wait function failed: %d\n",GetLastError());
return nullptr;
}
if(pInst==nullptr){
pInst=new sgt3;
}
}
SetEvent(hEvent);
return pInst;
}
sgt3(){printf("%s\n",__FUNCTION__);}
~sgt3(){
if(pInst)delete pInst;
}
};
sgt3* sgt3::pInst=nullptr;
int main(void){
hEvent=CreateEvent(nullptr,TRUE,TRUE,_T("hello"));
if(hEvent==INVALID_HANDLE_VALUE){
printf("CreateEvent failed:%d\n",GetLastError());
return 1;
}
sgt3::GetInstance();
CloseHandle(hEvent);
return 0;
}
还有一个问题,那就是在某些多核cpu的机器上,因为不同的cpu核可能访问pInst这个指针,而new出来pInst指针的cpu核心可能只是把这个pInst放在了寄存器或者告诉缓存里面,加上volatile可以保证不在寄存器里面,但是不能保证是在当前的cpu高速缓存里面还是在机器的内存里面。因此需要再次引入一个"禁止优化内存屏障"的机制。这个机制如果用C/C++编码的话,又是和平台有关的。Windows和Linux平台下VC/GCC的源码分别是:
- #include "stdafx.h"
- #include<Windows.h>
- CRITICAL_SECTION cs;
- class Singleton{
- static Singleton* pInst;
- public:
- static Singleton* GetInstance(){
- if(pInst==nullptr){
- EnterCriticalSection(&cs);
- if(pInst==nullptr){
- auto p=new Singleton();//创建
- MemoryBarrier();//同步
- pInst=p;//赋值
- }
- LeaveCriticalSection(&cs);
- }
- return pInst;
- }
- Singleton(){printf("%s\n",__FUNCTION__);}
- static void DestroyInstance(){
- if(pInst)delete pInst;
- }
- };
- Singleton* Singleton::pInst=nullptr;
- int main(void)
- {
- InitializeCriticalSection(&cs);
- auto p=Singleton::GetInstance();
- return 0;
- }
#include "stdafx.h"
#include<Windows.h>
CRITICAL_SECTION cs;
class Singleton{
static Singleton* pInst;
public:
static Singleton* GetInstance(){
if(pInst==nullptr){
EnterCriticalSection(&cs);
if(pInst==nullptr){
auto p=new Singleton();//创建
MemoryBarrier();//同步
pInst=p;//赋值
}
LeaveCriticalSection(&cs);
}
return pInst;
}
Singleton(){printf("%s\n",__FUNCTION__);}
static void DestroyInstance(){
if(pInst)delete pInst;
}
};
Singleton* Singleton::pInst=nullptr;
int main(void)
{
InitializeCriticalSection(&cs);
auto p=Singleton::GetInstance();
return 0;
}
Linux下面:
- #include <stdio.h>
- #include <pthread.h>
- pthread_mutex_t mutex;
- class Singleton{
- static Singleton* pInst;
- public:
- static Singleton* GetInstance(){
- //gcc下面的Meyers Singleton是安全的,无需Double checked lock.
- if(pInst==nullptr){
- auto p=new Singleton();
- pthread_mutex_lock(&mutex);//已经包含了memory barrier机制,不需要再增加代码
- //__asm__("mfence")或者__sync_synchronize()。IA32e(x86/x64)没有这个问题
- pInst=p;
- pthread_mutex_unlock(&mutex);
- }
- return pInst;
- }
- Singleton(){printf("%s\n",__FUNCTION__);}
- static void DestroyInstance(){
- if(pInst)delete pInst;
- }
- };
- Singleton* Singleton::pInst=nullptr;
- int main(void){
- if(pthread_mutex_init(&mutex,nullptr)!=0){
- printf("pthread_mutex_init failed\n");
- return 1;
- }
- Singleton::GetInstance();
- return 0;
- }
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex;
class Singleton{
static Singleton* pInst;
public:
static Singleton* GetInstance(){
//gcc下面的Meyers Singleton是安全的,无需Double checked lock.
if(pInst==nullptr){
auto p=new Singleton();
pthread_mutex_lock(&mutex);//已经包含了memory barrier机制,不需要再增加代码
//__asm__("mfence")或者__sync_synchronize()。IA32e(x86/x64)没有这个问题
pInst=p;
pthread_mutex_unlock(&mutex);
}
return pInst;
}
Singleton(){printf("%s\n",__FUNCTION__);}
static void DestroyInstance(){
if(pInst)delete pInst;
}
};
Singleton* Singleton::pInst=nullptr;
int main(void){
if(pthread_mutex_init(&mutex,nullptr)!=0){
printf("pthread_mutex_init failed\n");
return 1;
}
Singleton::GetInstance();
return 0;
}