//不包含成员初始化的Singleton
CObjFactory& CObjFactory::Instance()
{
static CObjFactoryinstance;
return instance;
}
//包含成员初始化的Singleton
CEngineManager& 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也很不错!