本文共 6359 字,大约阅读时间需要 21 分钟。
目录
最近项目里边用到了ACE, 里边的很多类都使用了ACE_Singleton(爱立信不少遗留项目,都喜欢用这种比较heavy的组件 :-()。
单件模式的原理虽然简单,易懂,但感觉要用好它,也不是那么容易。下面从几个方面来进行说明。
下面代码是单件模式的最基本的实现,仅限于单线程环境。 通过定义一个静态的成员,来保存这个唯一的对象实例。并通过一个静态接口来获取这个唯一的实例。
class Singleton{public: ~Singleton() { cout<<"Singleton Destructor"<
运行结果为:
Singleton constructor unique instance Press any key to continue . . .有了new, 就应该有对应的delete. 那到底什么时候释放呢。因为new分配的是堆内存,估计即使加上析构函数,也没有效果。带着这个疑问,做了下实验,给类Singleton加上了一个析构函数。
Singleton::~Singleton(){ std::cout<<"Singleton destructor"<
实验结果显示推测正确,堆内存不会自动释放。
自然的,会想到定义一个释放函数的方法,调用delete Singleton::instance()对静态实例进行释放。但是,这样增加了风险,因为调用者很可能会忘记调了此函数。 调研了些资料,其实有下面的几种方法:通过一个内嵌类和一个静态成员来实现自动释放的机制,相当于为单件加了个垃圾回收器。关键点在于static Cleaner clr;这个声明,由于是静态成员,系统会在栈里分配内存,回收工作也就由系统自动完成了。
class Singleton{public: ~Singleton() { cout<<"Singleton Destructor"<
运行结果为:
Singleton Constructor Cleaner Constructor unique instance Press any key to continue . . . 按任意键退出后,观察到有下面打印: Cleaner Destructor Singleton Destructor前面提到的方法,需要新增一个嵌套类,增加了复杂度。
可以对第1节(基本实现)中的getInstance方法进行优化,返回一个局部静态变量。static Singleton* getInstance() { static Singleton instance; return &instance; }
程序在结束的时候,系统会自动回收所有的静态/全局内存,这样,单件模式中的静态成员就能被析构掉了。
对于第1节(基本实现)中,单例是通过new方法分配的。可能有人会疑问:进程退出时,os会自动回收进程所占用的各种资源,包括栈内存,堆内存,代码指令等等。那为何还会存在第2节所说的资源未释放问题呢?进程退出时,os不就自动释放了么?
其实,有下面这些因素需要考虑: --现代的大部分os,如windows, linux的确会自动释放,但在一些嵌入式os中,不是自动回收的。 --如果代码是内核级的一些驱动,它造成的内存错误,也是无法自动回收的。除非重启os。 --良好的编程习惯约束,本程序分配的所有资源,都应该由程序自己去析构或者释放。这个c++开发者的最基本素养。
所谓线程安全,就是说如果代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 基于第1节中的基本实现,来验证下它是否线程安全:
#include#include #include using namespace std;class Singleton{public: ~Singleton() { cout<<"Singleton Destructor"< DoSomething(); return 0;}Singleton* Singleton::_instance = NULL;int main( int argc, void* argv[] ){ for( int i = 0; i < 10; ++i ) { HANDLE t = (HANDLE)_beginthreadex( NULL, 0, thread, NULL, 0, NULL ); CloseHandle(t); } system("pause"); return 0;}
运行结果为(注意:下面看似格式有些错乱,其实是程序的原始输出,并不是排版问题):
Singleton Constructor:1 Singleton DoSomething Singleton Constructor:5Singleton Constructor:3 Singleton DoSomething Singleton Constructor:4 Singleton DoSomething Singleton DoSomething Singleton DoSomething Singleton Constructor:6 Singleton DoSomething Singleton Constructor:2 Singleton DoSomething
Singleton DoSomething Singleton DoSomething Singleton DoSomething Press any key to continue
也称为慢初始化方法。即第一次调用该类实例的时候才产生一个新的该类实例,并在以后仅返回此实例。
此种方法,需要用锁来保证其线程安全性。因为如上3.1所示,多个线程可能会同时进入到判断实例是否存在的if语句中,它是非线程安全的。方案一:Double-Check
可以使用double-check来保证线程安全,这也是ACE_Singleton采用的方法。但是如果处理大量数据时,该锁可能会成为严重的性能瓶颈。
实现原理如下:Singleton* Singleton::getInstance() { if(NULL == _instance) { Lock(); if(NULL == _instance) _instance = new Singleton(); Unlock(); } return _instance; }
#include#include #include using namespace std;class Singleton{public: ~Singleton() { cout<<"Singleton Destructor"< DoSomething(); return 0;}Singleton* Singleton::_instance = NULL;HANDLE Singleton::mtx=INVALID_HANDLE_VALUE;int main( int argc, void* argv[] ){ Singleton::mtx = CreateMutex(NULL, FALSE, (LPCWSTR)"Mutex"); if(NULL==Singleton::mtx) return 0; for( int i = 0; i < 10; ++i ) { HANDLE t = (HANDLE)_beginthreadex( NULL, 0, thread, NULL, 0, NULL ); CloseHandle(t); } Sleep(100); CloseHandle(Singleton::mtx); system("pause"); return 0;}
Press any key to continue . . .
方案二:内部静态实例
此方法,就是2.2中所介绍的方法的改进。实现原理如下:
#include运行结果为: Singleton Constructor Singleton DoSomething Singleton DoSomething Singleton DoSomething Singleton DoSomething Singleton DoSomething Press any key to continue . . .#include #include using namespace std;class Singleton{public: ~Singleton() { cout<<"Singleton Destructor"< DoSomething(); return 0;}Singleton* Singleton::_instance = NULL;int main( int argc, void* argv[] ){ for( int i = 0; i < 5; ++i ) { HANDLE t = (HANDLE)_beginthreadex( NULL, 0, thread, NULL, 0, NULL ); CloseHandle(t); } Sleep(10); system("pause"); return 0;}
#include运行结果如下: Singleton Constructor Singleton DoSomething Singleton DoSomething Singleton DoSomething Singleton DoSomething Singleton DoSomething Press any key to continue . . .#include #include using namespace std;class Singleton{public: ~Singleton() { cout<<"Singleton Destructor"< DoSomething(); return 0;}Singleton* Singleton::_instance = new Singleton;int main( int argc, void* argv[] ){ for( int i = 0; i < 5; ++i ) { HANDLE t = (HANDLE)_beginthreadex( NULL, 0, thread, NULL, 0, NULL ); CloseHandle(t); } Sleep(10); system("pause"); return 0;}
使用单件模式时,需要注意的几个方面
任意两个 Singleton 类的构造函数不能相互引用对方的实例, 否则会导致程序崩溃. 如:
SingletonA& SingletonA::Instance() { const SingletonB& b = SingletonB::Instance(); static SingletonA theSingleton; return theSingleton; } SingletonB& SingletonB::Instance() { const SingletonA & b = SingletonA::Instance(); static SingletonB theSingleton; return theSingleton; }
在多线程的应用场合下必须小心使用。如果唯一实例尚未创建时, 有两个线程同时调用创建方法, 且它们均没有检测到唯一实例的存在, 便会同时各自创建一个实例, 这样就有两个实例被构造出来, 从而违反了单例模式中实例唯一的原则。 解决办法即第3节所讲。
多个 Singleton 实例相互引用的情况下, 需要谨慎处理析构函数. 如: 初始化顺序为 SingletonA » SingletonB » SingletonC 的三个 Singleton 类, 其中 SingletonA SingletonB 的析构函数调用了 SingletonC 实例的成员函数, 程序退出时, SingletonC 的析构函数 将首先被调用, 导致实例无效, 那么后续 SingletonA SingletonB 的析构都将失败, 导致程序异常退出。
转载地址:http://bleji.baihongyu.com/