在Web服务中使用singleton模式
singleton模式是一个使用非常广泛的模式,大凡在应用中如果只想存在某个对象的一个实例,并有时会通过该实例来共享一些信息,都会对该对象使用该模式,最简单的一种表现如下代码:
2 {
3 private singletonClass()
4 { }
5
6 private singletonClass m_innerClass = null;
7
8 public static singletonClass Instance()
9 {
10 if (m_innerClass == null)
11 {
12 m_innerClass = new singletonClass();
13 return m_innerClass;
14 }
15 return m_innerClass;
16 }
17
18 public void DoSomething()
19 { }
20
21 }
调用代码:
singletonClass instanceOfClass = singletonClass.Instance().DoSomething();
呵呵,大家发现上面这段代码的问题了吗?
当然,对于单线程访问,该代码是没有问题的,但是如果两个线程同时访问,会出现什么样的状况呢?
注意这段代码:
10 if (m_innerClass == null)
11 {
12 m_innerClass = new singletonClass();
13 return m_innerClass;
14 }
15 return m_innerClass;
如果 m_innerClass已经有值了,没问题,不论多少个线程都是返回同一个引用,但是如果几个线程同时进入到if(m_innerClass == null),
这时他们都会判断得到为true的结果,所以他们会同时进入下面的m_innerClass = new singletonClass ();代码,他们分别new了自己的对象
,所以没有达到单例的目的,这种情况对于使用单例模式来共享信息的应用来说,是有害的,因为这时不同线程引用了不同的实例。
如何解决这个问题呢?学习过操作系统的人可能都知道有个概念叫临界区,操作系统管理了这个计算机的所有资源,资源有有限的,不能
说哪个应用程序申请就马上给谁,是需要进行资源调度的,所以各个进程都在争着使用系统资源,及进入‘临界区’,但是同一时刻,能进入
临界区的只有一个进程,一旦有进程进入,其他进程只有在外面等,所以这样就保证了不会因资源争用产生混乱。同样这个道理也可以应用到
我们现在讲的单例模式中来。我门可以把我们的某段代码作为临界区,某一时刻只有一个线程可以进入:如下代码:
2 {
3 private singletonClass()
4 { }
5
6 private singletonClass m_innerClass = null;
7 private object m_syn = new object();
8
9 public static singletonClass Instance()
10 {
11 lock (m_syn)
12 {
13 if (m_innerClass == null)
14 {
15 m_innerClass = new singletonClass();
16 return m_innerClass;
17 }
18 return m_innerClass;
19 }
20 }
21
22 public void DoSomething()
23 { }
24
25 }
使用lock将我们要使用的那段代码锁住,用完以后再释放,这样的话就不会有多个线程同时进入代码了吧。
我们在写WebService的时候时常是把实现逻辑放到另外的dll,一方面便于进行单元测试,另一方面将业务代码和‘外观’分离也是一种
良好的习惯。这时,我们可能会有这样的一个实现,就是需要通过Webservice的一个WebMethod获得一个单例工厂,然后使用该工厂生成一个
能实现某种功能的实例。如果没注意的话可能会有这样的用法:
[WebMethod]
public string DoSomething()
{
SingleTonFactory fac = SingleTonFactory.Instance();//获得一个单例工厂实例
return ((IABC)fac.GetABC()).GetString(); //通过该实例获得一个IABC接口,调用接口的GetString()方法返回一个字符串
}
这段通过WebMethod方式使用单例工厂有什么问题呢?
如果仔细想想不难发现,WebMethod是SingleCall模式的,即调用一次方法会实例化话一个对象,所以在每个服务方法调用完之后该对象都
会消失,下次调用,又会再实例化一个对象,所以在两次服务之间没有保存状态,上次实例话的对象消失以后,其使用的工厂由于没有再被引
用,也会消失,所以每次web访问都会重新new一个工厂出来,其效果如同每次web访问直接new一个工厂出来,这样不就失去了单例工厂的意义
了吗?
不要急,还有Applicaiton和Session可以保存状态,我们只要让一个全局的对象引用到工厂,他就不会消失,其实例就可以得到重用。
我们在WebService启动的时候将其实例放到Application中,APPlication["SingleTonFactory "] = SingleTonFactory.Instance();这样
WebMethod中就可以可以直接使用(SingleTonFactory )APPlication["SingleTonFactory "] 来调用。然后在Web应用结束的时候使用
APPlication["SingleTonFactory "]=null;把资源释放掉。
OK,问题解决了,不知道各位还有什么高招来解决相同问题,或是有Singleton模式的其他应用,欢迎大家讨论。