复杂的Singleton

来源:互联网 发布:7u分享网络app 编辑:程序博客网 时间:2024/06/05 02:40
//不包含成员初始化的SingletonCObjFactory& CObjFactory::Instance(){ static CObjFactoryinstance; return instance;}//包含成员初始化的SingletonCEngineManager& CEngineManager::Instance(){ static CEngineManagers_kbEngineMgr; try {  if(s_kbEngineMgr.m_bInited)  {            return s_kbEngineMgr;  }  else  {   s_kbEngineMgr.m_sec.Lock();   if(!s_kbEngineMgr.m_bInited)   {    s_kbEngineMgr.m_kbReasoningMgr.Release();    CInnerManager::Instance().Release();    s_kbEngineMgr.LoadBuffParam();   ...}


 

两种用法在单线程里都不会有什么问题
但是并发的时候 这两种方式都会存在严重的问题


 首先明确一点 static 定义的变量只会初始化一次
 
 先来看第一个例子
 加入有两个线程都调用了CObjFactory& CObjFactory::Instance();
 
 那么将会发生如下情况
 第一个线程执行到了static CObjFactoryinstance;
 调用了CObjFactory的构造函数 加入在构造函数执行到一般的时候发生了context swtich 构造函数并没有执行完毕
 而第二线程在这时进入了CObjFactory& CObjFactory::Instance()
 由于static的定义
 static CObjFactoryinstance;这句语句并不会被二次执行
 第二个线程直接返回return instance;
 
 如果这时对返回的对象调用成员函数会有如下两种情况
 1.没有访问成员变量的成员函数
  这不会造成什么问题
 2.有访问成员变量的成员函数
  程序崩溃
 
 原因很简单 由于第一个线程进入了CObjFactory的构造函数 但在构造函数还没执行完毕的时候发生了context switch 这时候对象的成员变量没有完全构造完成
 第二个线程抢占了执行权 这就有可能访问到尚未构造完成的成员变量 后果可想而知
 
 而二个Singleton的实现 自己对照第一种方式来解释 也将产生一样的问题


 造成bug的关键点在于构造函数是否完成没有一个明确的标识 不建议使用static 对象的方式来进行Singleton实现
      如果使用指针 根据指针是否为空则可以明确判断构造函数是否调用完成
  
 //remark reference from http://www.cppblog.com/ant/archive/2007/09/07/31786.html
 Double-Checked Locking机制看起来像是一个完美的解决方案,但是在某些条件下仍然不行。简单的说,编译器为了效率可能会重排指令的执行顺序(compiler-based reorderings)。看这一行代码:

 _instance = new Singleton();

 在编译器未优化的情况下顺序如下:
 1.new operator分配适当的内存;
 2.在分配的内存上构造Singleton对象;
 3.内存地址赋值给_instance。

 但是当编译器优化后执行顺序可能如下:
 1.new operator分配适当的内存;
 2.内存地址赋值给_instance;
 3.在分配的内存上构造Singleton对象。

 当编译器优化后,如果线程一执行到2后被挂起。线程二开始执行并发现0 == _instance为false,于是直接return,而这时Singleton对象可能还未构造完成,后果...

 上面说的还只是单处理器的情况,在多处理器(multiprocessors)的情况下,超线程技术必然会混合执行指令,指令的执行顺序更无法保障。关于Double-Checked Locking的更详细的文章,请看:
 The "Double-Checked Locking is Broken" Declaration
 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
 
 
Singleton的析构问题
到此Singleton已经算比较完善了,但是依然算不上完美,因为到现在只是解决了多线程问题,加入了模板支持,对于KDL problem(The Dead Reference Problem)依然没法解决,可以说在实现Singleton模式时,最大的问题就是多个有依赖关系的Singleton的析构顺序。虽然Modern C++ Design中给出了解决方案,但是Loki的实现太过复杂,在此就不详细说明了,有兴趣的可以看看Modern C++ Design,当然了,Loki库中用策略模式实现的Singleton也很不错!

 

 

原创粉丝点击