用户名: 密码: 验证码:           网站地图  高级搜索  RSS订阅  收藏本站
您的位置:主页 > 程序编程 > VB.NET >

Visual Basic.NET实现双检锁(DCL)模式

[ 来源:天极网 | 作者:阎宏博士 | 更新日期:2008-5-18 17:45:10 | 人气: | 评论 0 条 ]
  本文介绍了称为双检锁(Double-Check Locking简称DCL)模式的代码模式,它的工作原理及其在Singleton(单例)模式及Multiton(多例)模式中的应用,并且讨论了DCL模式在Visual Basic.NET和C#语言中的实现。其中Visual Basic.NET的源代码可以在文中看到,C#的源代码在附录中给出。

  本文假设读者熟悉Visual Basic.NET或C#的多线程概念、设计模式的基本概念,以及UML基本图标。

  DCL模式(Double-Check Locking Pattern)有时又称作双检模式(Double-Check Pattern),只有在多线程的环境中才有用。它是从C语言移植过来的。在C语言里,DCL模式经常用在多线程环境中类的迟实例化(Late Instantiation)里。

  DCL模式通常与Factory模式一同使用,用来循环使用产品对象。假如读者熟悉Singleton(Singleton)模式的话,DCL模式可以使用到"懒汉式"的Singleton模式里面,用来提供唯一的产品对象。通过进一步推广,可以使用到Multiton模式和Flyweight模式里面。

  从Factory模式谈起

  为了解释什么是DCL模式,还是从Factory模式谈起吧。

  在下面的类图中,工厂类Factory0有一个共享方法GetInstance()用来提供产品类Product的实例。 www.jc567.cn


图1、一个由工厂类与产品类组成的系统。

  Factory0的源代码如下:

Public Class Factory0
 Public Shared Function GetInstance() As Product
  Return New Product()
 End Function
End Class
代码清单1、Factory0类的源代码
jc567.cn
  显然,只要调用GetInstance()方法就会得到Product类的实例,每一次调用得到的都是新的实例。Product类非凡提供了计数的方法,通过调用GetCount()方法就可以得到Product所有实例的总数。

Public Class Product
Private Shared count As Integer = 0

Public Sub New()
 count = 1
 System.Console.WriteLine("Product number {0} is created.", count)
End Sub

Public Shared Function GetCount() As Integer
 Return count
 End Function
End Class
代码清单2、产品类Product的源代码

  但是假如产品类的实例必须循环使用,而不能无限制创建的话,工厂方法GetInstance()的内容必须改写,以实现必要的循环逻辑。而最简单的循环逻辑,就是重复使用单一的产品类实例。比如下面的源代码就实现了单一产品类实例的逻辑:
复制于jc567.cn


Public Class Factory1
Private Shared instance As Product

Public Shared Function GetInstance() As Product
 If (instance Is Nothing) Then
  instance = New Product()
 End If
 Return instance
End Function
End Class
代码清单3、工厂类Factory1的源代码

  简单得不能再简单了吧?假如已经创建过Product类实例的话,就返还这个实例;反之,就首先创建这个实例,将之记录在案,然后再返还它。

  写出这样的代码,本意显然是要保持在整个系统里只有一个Product 的实例;因此才会有 If (instance Is Nothing) Then 的检查。不很明显的是,假如在多线程的环境中运行,上面的代码会有两个甚至两个以上的Product对象被创建出来,从而造成错误。 jc567.cn

  在多线程环境里,假如有两个线程A和B几乎同时到达 If (instance Is Nothing) Then语句的外面的话,假设线程A比线程B早一点点,那么:

  1. A会首先进入If (instance Is Nothing) Then 块的内部,并开始执行New Product()语句。 至此时,instance变量仍然是Nothing,直到线程A的New Product()语句返回并给instance变量赋值。

  2. 但是,线程B并不会在If (instance Is Nothing) Then 语句的外面等待,因为此时instance Is Nothing是成立的,它会马上进入If (instance Is Nothing) Then语句块的内部。 这样,线程B会不可避免地执行instance = New Product()的语句,从而创建出第二个实例来。

  3. 下面,线程A的instance = New Product()语句执行完毕,instance变量得到了真实的对象引用, (instance Is Nothing)不再为真。第三个线程不会在进入If (instance Is Nothing) Then语句块的内部了。

  4. 紧接着,线程B的instance = New Product()语句也执行完毕,instance变量的值被覆盖。但是第一个Product对象被线程A引用的事实不会改变。

  这时,线程A和B各自拥有一个独立的Product对象,而这是错误的。为了能够直观地看到程序执行的结果,可以运行下面的客户端代码:

007网络教程网



Private Sub Run1()
 Dim o As Product
 o = Factory1.GetInstance
 System.Console.WriteLine("Total number of objects created: {0} ", o.GetCount)
End Sub

Private Sub btnCreate1_Click(…) Handles btnCreate1.Click
 Dim t(9) As Thread
 Dim count As Integer

 For count = 0 To 9
  t(count) = New Thread(AddressOf Run1)
  t(count).Start()
 Next
End Sub
代码清单4、客户端的源代码

  另外在Factory1的GetInstance()方法的第一行加入:

Thread.Sleep(10)
文章来源于www.jc567.cn

  的语句,相当于模拟一个冗长的产品创建过程,使得最早进入的线程等待后面的线程,从而凸显现多线程的问题。

  上面的客户端代码使用了10个线程同时调用工厂方法,然后调用产品的计数方法,打印出产品类的实例总数。假如读者运行一下这些代码的话,就会发现,工厂方法会创建出远多于1个的产品实例,在笔者运行这段代码时,系统整整产生了9个产品实例。

  因此Factory1作为循环使用产品实例的工厂在多线程环境中是失败的。使用类似于代码清单4的客户端进行试验的话,可以看出系统自始至终仅仅创建了一个产品实例。


Tags:Visual,Basic.NET实现双检锁(DCL)模式
您的评论
用户名: 新注册) 密码: 匿名评论 [所有评论]

·用户发表意见仅代表其个人意见,并且承担一切因发表内容引起的纠纷和责任
·本站管理人员有权在不通知用户的情况下删除不符合规定的评论信息或留做证据
·请客观的评价您所看到的资讯,提倡就事论事,杜绝漫骂和人身攻击等不文明行为