您的位置:首页 > 其它

Singleton Pattern 两种实现的选择

2010-01-04 18:40 155 查看
单件模式就不用多介绍了,其实现有两种主流的思路:Double Check和Static,两种方案的C#终极代码大致如下。

方案一:加锁

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还慢了一倍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: