关于DCLP实现的单例模式的一些想法
我之前写过单例的文章( http://www.cnblogs.com/mkdym/p/4908644.html ),但是现在又有了一些想法,不想再在原来那篇文章上更新,所以独开一篇。
看到一些书或者文章讲单例模式的时候,总要说起DCLP,并且得出的结论是DCLP是不可靠的,也就不能用它来实现单例。即下面这样做是错误的:
class SingletonAA { public: static SingletonAA& get_instance_ref() { if (NULL == p_) // 1st check { scoped_lock lock; if (NULL == p_) // 2nd check { p_ = new SingletonAA(); } } return *p_; } private: SingletonAA() { //... } ~SingletonAA() { //... } private: static SingletonAA *p_; }; SingletonAA *SingletonAA::p_ = NULL;
因为new那一句不是原子的,分了3步,而且顺序不一定,这个顺序又会影响第一次检查的结果。接着他们又讨论了volatile关键字和乱序优化,然后得出结论这个关键字也不能使DCLP变得正确,或者说不能轻松的使DCLP变得正确。
但是他们讲这个问题的时候全部的前提是用单例对象的指针本身去做了判断条件,这就是诱因:单例对象的创建和单例的判断是对同一个元素读写,而“写”太“复杂”了!
那么我不用单例对象去做判断不就行了吗?如下:
class SingletonAA { public: static SingletonAA& get_instance_ref() { if (!init_flag_) // 1st check { scoped_lock lock; if (!init_flag_) // 2nd check { p_ = new SingletonAA(); init_flag_ = true; } } return *p_; } private: //... private: static SingletonAA *p_; static bool volatile init_flag_; }; SingletonAA *SingletonAA::p_ = NULL; bool volatile SingletonAA::init_flag_ = false;
我换用一个bool标志,bool变量在vc上是一个字节的,操作只需一条指令,是原子的。而且声明成volatile,确保编译器不对它做优化。
按我的不准确的知识,编译器或CPU可能会对
p_ = new SingletonAA();
和
init_flag_ = true;
这两句调整顺序,因为他们两句没有关联,这就又会出错。那么我制造一个关联:
init_flag_ = p_ ? true : false;
使init_flag_的赋值依赖于对象的创建,按我的理解,此时编译器和CPU都应该使设置标志语句后于对象创建语句,那么就没有错误了。
假如编译器认为p_在执行完new后一定不为0,那么它就又可以优化init_flag_的赋值了。那么这种情况会不会出现,编译器是否会这样认为?
Need Help
Need Help
Need Help
(重说三)
因为我想得到大家的关注,所以可耻的勾选了“发布至博客园首页”,如果最后没有在首页出现,我就把这句话删掉,免的丢我的小脸。