最常见的程序员面试题(4)写一个能工作的Singleton

单件设计模式是非常常用的一种设计模式。当我有一个复杂的系统需要提供一个统一的配置界面的时候,这个配置对象和后台对象之间的交互常常是一对多的关系。那么配置对象常常就需要被设计成Singleton的实例。

        最简单的singleton就是用一个全局对象或者函数内部的静态对象来实现。但是这样有两个明显的问题。(1) 全局对象是程序初始化的时候就构造的,如果程序启动要构造很多此类对象,会变得很昂贵。(2) C++标准并没有规定不同的编译单元的全局对象的构造顺序,所以如果不同Singleton之间的构造有相互依赖关系,那么行为是未知的。

  1. struct sgt1{  
  2.     static sgt1* GetInstance(){  
  3.         static sgt1 Inst;  
  4.         return &Inst;  
  5.     }  
  6. };  
  7. struct sgt2{  
  8.     static sgt2* pInst;  
  9.     static sgt2* GetInstance(){  
  10.         if(pInst==nullptr){  
  11.             pInst=new sgt2;  
  12.         }  
  13.         return pInst;  
  14.     }  
  15.     ~sgt2(){  
  16.         if(pInst)delete pInst;  
  17.     }  
  18. };  
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类型了。

  1. HANDLE hEvent;  
  2. struct sgt3{  
  3.     static sgt3* pInst;  
  4.     static sgt3* GetInstance(){  
  5.         if(pInst==nullptr){  
  6.             if(WaitForSingleObject(hEvent,INFINITE)!=WAIT_OBJECT_0){  
  7.                 printf("Wait function failed: %d\n",GetLastError());  
  8.                 return nullptr;  
  9.             }  
  10.             if(pInst==nullptr){  
  11.                 pInst=new sgt3;  
  12.             }  
  13.         }  
  14.         SetEvent(hEvent);  
  15.         return pInst;  
  16.     }  
  17.     sgt3(){printf("%s\n",__FUNCTION__);}  
  18.     ~sgt3(){  
  19.         if(pInst)delete pInst;  
  20.     }  
  21. };  
  22. sgt3* sgt3::pInst=nullptr;  
  23. int main(void){  
  24.     hEvent=CreateEvent(nullptr,TRUE,TRUE,_T("hello"));  
  25.     if(hEvent==INVALID_HANDLE_VALUE){  
  26.         printf("CreateEvent failed:%d\n",GetLastError());  
  27.         return 1;  
  28.     }  
  29.     sgt3::GetInstance();  
  30.     CloseHandle(hEvent);  
  31.     return 0;  
  32. }  
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的源码分别是:

  1. #include "stdafx.h"   
  2. #include<Windows.h>   
  3. CRITICAL_SECTION cs;  
  4. class Singleton{   
  5.      static Singleton* pInst;   
  6. public:   
  7.      static Singleton* GetInstance(){   
  8.          if(pInst==nullptr){   
  9.              EnterCriticalSection(&cs);   
  10.              if(pInst==nullptr){   
  11.                  auto p=new Singleton();//创建   
  12.                  MemoryBarrier();//同步   
  13.                  pInst=p;//赋值   
  14.              }   
  15.              LeaveCriticalSection(&cs);   
  16.          }  
  17.          return pInst;  
  18.      }   
  19.      Singleton(){printf("%s\n",__FUNCTION__);}   
  20.      static void DestroyInstance(){   
  21.          if(pInst)delete pInst;   
  22.      }   
  23. };   
  24. Singleton* Singleton::pInst=nullptr;   
  25. int main(void)  
  26. {  
  27.     InitializeCriticalSection(&cs);  
  28.     auto p=Singleton::GetInstance();  
  29.     return 0;  
  30. }  
#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下面:

  1. #include <stdio.h>    
  2. #include <pthread.h>    
  3. pthread_mutex_t mutex;   
  4. class Singleton{   
  5.      static Singleton* pInst;   
  6. public:   
  7.      static Singleton* GetInstance(){  
  8.          //gcc下面的Meyers Singleton是安全的,无需Double checked lock.   
  9.          if(pInst==nullptr){   
  10.                auto p=new Singleton();  
  11.                pthread_mutex_lock(&mutex);//已经包含了memory barrier机制,不需要再增加代码   
  12.                  //__asm__("mfence")或者__sync_synchronize()。IA32e(x86/x64)没有这个问题   
  13.                 pInst=p;  
  14.               pthread_mutex_unlock(&mutex);  
  15.          }   
  16.          return pInst;  
  17.      }   
  18.      Singleton(){printf("%s\n",__FUNCTION__);}   
  19.      static void DestroyInstance(){   
  20.          if(pInst)delete pInst;   
  21.      }   
  22. };   
  23. Singleton* Singleton::pInst=nullptr;   
  24. int main(void){   
  25.      if(pthread_mutex_init(&mutex,nullptr)!=0){   
  26.          printf("pthread_mutex_init failed\n");   
  27.          return 1;   
  28.      }   
  29.      Singleton::GetInstance();   
  30.      return 0;   
  31. }  
#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; 
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值