使用单件设计模式需要注意的地方
单件设计模式可以让类的对象实例在一个进程中只有一个,而且提供了比较简洁的访问该对象的方式。
这些是单件模式的优点,但是单件也有两个比较麻烦的缺点,这里拿出来和大家分享:
1,多线程问题:
单件的通常设计成在获取对象实例的时候才创建(方便使用),但是在多线程的环境下,可能同一时刻多个线程调用获取实例方法,所以,如果保护不好很容易造成不在单件了,而是生成了很多个事例。所以单件事例在创建的时候需要加琐保护。
由于加琐会增加获取单件实例的运行负荷,所以通常我们需要对加琐进行优化。
琐的优化通常就是常听说的双检测锁的方式;
2,资源释放问题:
单件事例的资源释放问题也是比较麻烦的,Gof的书中只讲了如何实现单件,如何保持类只实例化一个对象,但是对单件实例的资源释放问题避而不谈。
由于单件实例往往使用了静态数据,通常是个静态指针,而且提供一个静态的方法来获取/生成实例。这个实例通常是new出来的,使用这个实例的对象可能有很多个,于是问题来了:
这个单件实例应该如何释放呢?
能想到的可能会有下面几种方法:
1)采用引用记数,让单件的获取方,获取后自己释放;这个方法避免不了反复创建和释放单件实例,而且某一个使用者忘记释放,单件实例就再也释放不了了。
2)由一个模块负责释放单件实例,但是一旦这个模块在进程中加载多份,就又变得麻烦了,模块的销毁有先后顺序,前一个模块将单件释放了,但是后一个不知道单件已经“挂”了,如果这种场景在多线程情况下,会是什么样子,就很难说了。
这个问题折腾了我很久,最后我自己选择了静态内存释放对象来解决这个问题,这样,当进程退出的时候,静态指针被析构,此时再释放单件实例就安全了,而中间无论有多少个模块或者模块实例访问都没问题。
而且这个静态内存释放对象是完全可以屏蔽到接口内部的,不必暴露出来。
------解决方案--------------------CAutoReleaser的用法也要注意,如果用他来管理一个指针,使用方还没有释放,你先把他释放了。