Singleton Pattern 两种实现的选择
2010-01-04 18:40
155 查看
单件模式就不用多介绍了,其实现有两种主流的思路:Double Check和Static,两种方案的C#终极代码大致如下。
方案一:加锁
方案二:静态构造
方案一的代码还不够终结,有一些特殊的关键字没有加,不过结构就是这样了。这是最传统的思路,加锁来防止并发,演化为使用双判断来最大化的避免并发。优点是稳重可靠,缺点是使用锁损耗性能。
方案二的思路是利用静态构造的特性,来保证成员只会被加载一次。很巧妙,但是有个缺点是所需的单件实例加载的会太早,于是有了这个终极方案,使用嵌套类来延迟加载。更加巧妙,优点很显然,性能没有缺陷,缺点么,只能在静态构造器中实例化。
我以前是很喜欢方案二的,讨厌加锁,而且利用静态构造器自己琢磨出来过,很有感情。所以仅从代码实现面上考虑,方案二绝对好于一。
但是方案二有个致命的缺陷,至少我一直没有办法弥补:
如果初始化时失败了,如何恢复?
假设我们用方案二提供一个单件的消息服务连接,当第一次访问的时候,网络恰巧断线了,创建连接失败,对象为null。当第二次访问的时候,当然还是null,如何恢复呢?因为这时候无法再利用静态构造器了,难道你要抛个异常告诉Admin,请重启程序来重新连接么。只好每次访问的时候都判断并实例化,显然这需要加锁的,又回到了第一种方案上了。或者使用标志来记录状态、或使用循环来自我恢复,还是都需要加锁的,只是位置不同而已。何况,即便是初始化成功,第N次连接失败,第N+1次也是应该尝试恢复的。
那么还有一种场景也不好用方案二,就是提供的这个单件需要释放对象、更改状态。方案二是无法提供这些功能的。道理很显然,更改了之后你无法恢复。
所以,两者的比较要补充:Double Check的适用面是完美的,性能有损;Static的性能是完美的,适用不强。
凡是需要对外提供更改状态、或者会有异常引起内部状态变更的服务,都不适用方案二来实现。换句话说,绝对只读的、异常可控的单件服务,才适合使用方案二实现。其他的情况,您还是老老实实的用锁吧。毕竟需求第一,性能靠后。
当然,按照大师们所说,若是您能够把单件模式都重构掉可以不用,那是最好不过了。
-------------
今天居然不加班了,影响了计划。
写点小东西找找感觉,结果CSDN很卡,没感觉。而且用IE打开比用FF还慢了一倍。
方案一:加锁
public sealed class Singleton { static Singleton instance=null; static readonly object lockHelp = new object(); Singleton() { } public static Singleton Instance { get { if (instance==null) { lock (lockHelp ) { if (instance==null) { instance = new Singleton(); } } } return instance; } } }
方案二:静态构造
public sealed class Singleton { Singleton() { } public static Singleton Instance { get { return LazyHelp.instance; } } class LazyHelp { static LazyHelp() { } internal static readonly Singleton instance = new Singleton(); } }
方案一的代码还不够终结,有一些特殊的关键字没有加,不过结构就是这样了。这是最传统的思路,加锁来防止并发,演化为使用双判断来最大化的避免并发。优点是稳重可靠,缺点是使用锁损耗性能。
方案二的思路是利用静态构造的特性,来保证成员只会被加载一次。很巧妙,但是有个缺点是所需的单件实例加载的会太早,于是有了这个终极方案,使用嵌套类来延迟加载。更加巧妙,优点很显然,性能没有缺陷,缺点么,只能在静态构造器中实例化。
我以前是很喜欢方案二的,讨厌加锁,而且利用静态构造器自己琢磨出来过,很有感情。所以仅从代码实现面上考虑,方案二绝对好于一。
但是方案二有个致命的缺陷,至少我一直没有办法弥补:
如果初始化时失败了,如何恢复?
假设我们用方案二提供一个单件的消息服务连接,当第一次访问的时候,网络恰巧断线了,创建连接失败,对象为null。当第二次访问的时候,当然还是null,如何恢复呢?因为这时候无法再利用静态构造器了,难道你要抛个异常告诉Admin,请重启程序来重新连接么。只好每次访问的时候都判断并实例化,显然这需要加锁的,又回到了第一种方案上了。或者使用标志来记录状态、或使用循环来自我恢复,还是都需要加锁的,只是位置不同而已。何况,即便是初始化成功,第N次连接失败,第N+1次也是应该尝试恢复的。
那么还有一种场景也不好用方案二,就是提供的这个单件需要释放对象、更改状态。方案二是无法提供这些功能的。道理很显然,更改了之后你无法恢复。
所以,两者的比较要补充:Double Check的适用面是完美的,性能有损;Static的性能是完美的,适用不强。
凡是需要对外提供更改状态、或者会有异常引起内部状态变更的服务,都不适用方案二来实现。换句话说,绝对只读的、异常可控的单件服务,才适合使用方案二实现。其他的情况,您还是老老实实的用锁吧。毕竟需求第一,性能靠后。
当然,按照大师们所说,若是您能够把单件模式都重构掉可以不用,那是最好不过了。
-------------
今天居然不加班了,影响了计划。
写点小东西找找感觉,结果CSDN很卡,没感觉。而且用IE打开比用FF还慢了一倍。
相关文章推荐
- 文件夹选择对话框 JS实现的两种方案
- 利用Query+bootstrap和js两种方式实现日期选择器
- memcached 和 mysql 结合使用的两种实现选择?
- 文件夹选择对话框 JS实现的两种方案
- 两种实现模式,还是选择2,少一层继承。
- 文件夹选择对话框 JS实现的两种方案
- 选择排序的两种实现方法
- memcached 和 mysql 结合使用的两种实现选择?
- 黑马程序员——Java基础 数组排序的两种实现方法,选择和冒泡
- shell实现菜单选择的两种方法
- 分享实现Android图片选择的两种方式
- 文件夹选择对话框 JS实现的两种方案
- DropDownList实现可输入可选择(两种版本可选)
- 线程的两种实现方式
- U盘安装centos6.5教程(两种实现方法)
- java 实现文件复制的两种方式
- Android中ClearEditText实现点击EditText输入框右边清除图标来清除输入内容的两种方式
- 导入Excel到DataTable中的两种实现
- NumberPicker实现时间选择器,并且可以选择一定范围内的时间
- 算法--实现1+2+3...+n,要求不能使用乘除法、循环、条件判断、选择相关的关键字。(详细讲解)