Singleton之C++部分一

采用静态或者全局变量的实现方案

由于C++不能保证静态或者全局对象的构造函数的调用顺序以及析构顺序。所以如果程序中有多个用此方法实现的Singleton类,它们之间又有某种构造依赖关系和析构依赖关系,就会造成灾难性的后果。所以,只有当肯定不会有构造和析构依赖关系的情况下,这种实现才是合适的。

> 优点 实现简单,多线程下安全
> 缺点 如果有多个Singleton对象的创建顺序有依赖时,千万别用;不是lazy loading,有些浪费。

Meyers Singleton来控制构造顺序,但是不能控制析构顺序

Scott Meyer在<<Effective C++>>3rd Item4中提出了一个解决方案,当将non-local static变量移动到静态方法中成为local static变量的时候。C++保证当第一次静态方法被调用的时候,才会创建该静态变量。但是这里有一个疑问,创建顺序能够被控制了,可是析构顺序呢?我们只知道进程结束的时候,local static 变量会被析构,而且按照创建顺序的相反顺序进行。如果几个Singleton类的析构函数之间也有依赖关系,并且这种依赖顺序关系和LIFO顺序冲突,就会造成dead-reference问题。

> 优点 实现简单;用的时候才创建,比较节省。
> 缺点 多线程下不安全;如果有多个Singleton对象的析构顺序有依赖时,要小心

DCLP 98标准下是不可靠的,0x标准下是可靠的

DCLP 就是 Double-checked locking pattern.用于在多线程环境下保证只创建Singleton对象。第一次check不用加锁,但是第二次check和创建对象必须加锁。还要注意编译器可能会优化代码,导致DCLP模式失效。因此要使用volatile 修饰T* pInstance变量。先看一下 DCLP的实现代码:

  1. class Singleton {  
  2. public:  
  3.     static Singleton* instance() {  
  4.         if (pInstance == 0) {  
  5.             Lock lock;  
  6.             if (pInstance == 0) {  
  7.                 pInstance = new Singleton;  
  8.             }  
  9.         }  
  10.         return pInstance;  
  11.     }  
  12. private:  
  13.     static Singleton * volatile pInstance;  
  14.     Singleton(){  
  15.     }  
  16. };  


客户调用时,在每个线程的开头都获得Singleton* p = Singleton::instance();以后就一直使用这个p变量,应该说还是能有效的降低同步的机会。避免频繁调用Singleton::instance()->就好。

> 优点 实现简单,线程安全
> 缺点 客户需要意识到,并且遵守少调用的原则。

 

程序开始之前创建Singleton对象

严格来说,这是个策略。将要初始化的放到程序最开始初始化。在这个策略下,以上几种方案都是可以的,包括DCLP。因为总是在单线程中创建对象


posted on 2012-04-19 15:20  Hibernate4  阅读(209)  评论(0编辑  收藏  举报

导航