SoRoMan

人若无名,便可专心练剑.

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  12 随笔 :: 1 文章 :: 41 评论 :: 0 Trackbacks

<转贴-To Me>
概述

Singleton模式 

五种实现

1 .简单实现

 1 public   sealed   class  Singleton
 2 {
 3      static  Singleton instance = null ;
 4
 5     Singleton()
 6      {
 7     }

 8
 9      public   static  Singleton Instance
10      {
11          get
12          {
13              if  (instance == null )
14              {
15                 instance  =   new  Singleton();
16             }

17              return  instance;
18         }

19     }

20 }

这种方式的实现对于线程来说并不是安全的,因为在多线程的环境下有可能得到Sigleton类的多个实例。如果同时有两个线程去判断(instance == null),并且得到的结果为真,这时两个线程都会创建类Sigleton的实例,这样就违背了Sigleton模式的原则。实际上在上述代码中,有可能在计算出表达式的值之前,对象实例已经被创建,但是内存模型并不能保证对象实例在第二个线程创建之前被发现。

该实现方式主要有两个优点:

l         由于实例是在 Instance属性方法内部创建的,因此类可以使用附加功能(例如,对子类进行实例化),即使它可能引入不想要的依赖性。

l         直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton

2 .安全的线程

 1 public   sealed   class  Singleton
 2 {
 3      static  Singleton instance = null ;
 4      static   readonly   object  padlock  =   new   object ();
 5
 6     Singleton()
 7      {
 8     }

 9
10      public   static  Singleton Instance
11      {
12          get
13          {
14              lock  (padlock)
15              {
16                  if  (instance == null )
17                  {
18                     instance  =   new  Singleton();
19                 }

20                  return  instance;
21             }

22         }

23     }

24 }

25
26

这种方式的实现对于线程来说是安全的。我们首先创建了一个进程辅助对象,线程在进入时先对辅助对象加锁然后再检测对象是否被创建,这样可以确保只有一个实例被创建,因为在同一个时刻加了锁的那部分程序只有一个线程可以进入。这种情况下,对象实例由最先进入的那个线程创建,后来的线程在进入时( instence == null )为假,不会再去创建对象实例了。但是这种实现方式增加了额外的开销,损失了性能。

3 .双重锁定

 1 public   sealed   class  Singleton
 2 {
 3      static  Singleton instance = null ;
 4      static   readonly   object  padlock  =   new   object ();
 5
 6     Singleton()
 7      {
 8     }

 9
10      public   static  Singleton Instance
11      {
12          get
13          {
14              if  (instance == null )
15              {
16                  lock  (padlock)
17                  {
18                      if  (instance == null )
19                      {
20                         instance  =   new  Singleton();
21                     }

22                 }

23             }

24              return  instance;
25         }

26     }

27 }

28

这种实现方式对多线程来说是安全的,同时线程不是每次都加锁,只有判断对象实例没有被创建时它才加锁,有了我们上面第一部分的里面的分析,我们知道,加锁后还得再进行对象是否已被创建的判断。它 解决了线程并发问题,同时避免在每个 Instance 属性方法的调用中都出现独占锁定。它还允许您将实例化延迟到第一次访问对象时发生。实际上,应用程序很少需要这种类型的实现。大多数情况下我们会用静态初始化。 这种方式仍然有很多缺点:无法实现延迟初始化。

4 .静态初始化

 1 public   sealed   class  Singleton
 2 {
 3      static   readonly  Singleton instance = new  Singleton();
 4
 5      static  Singleton()
 6      {
 7     }

 8
 9     Singleton()
10      {
11     }

12
13      public   static  Singleton Instance
14      {
15          get
16          {
17              return  instance;
18         }

19     }

20 }

21

看到上面这段富有戏剧性的代码,我们可能会产生怀疑,这还是 Sigleton 模式吗? 在此实现中,将在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化。该类标记为 sealed 以阻止发生派生,而派生可能会增加实例。此外,变量标记为 readonly,这意味着只能在静态初始化期间(此处显示的示例)或在类构造函数中分配变量。

该实现与前面的示例类似,不同之处在于它依赖公共语言运行库来初始化变量。它仍然可以用来解决 Singleton模式试图解决的两个基本问题:全局访问和实例化控制。公共静态属性为访问实例提供了一个全局访问点。此外,由于构造函数是私有的,因此不能在类本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一的实例。

由于 Singleton 实例被私有静态成员变量引用,因此在类首次被对 Instance属性的调用所引用之前,不会发生实例化。

这种方法唯一的潜在缺点是,您对实例化机制的控制权较少。在Design Patterns形式中,您能够在实例化之前使用非默认的构造函数或执行其他任务。由于在此解决方案中由 .NET Framework 负责执行初始化,因此您没有这些选项。在大多数情况下,静态初始化是在 .NET 中实现 Singleton的首选方法。

5 .延迟初始化

 1 public   sealed   class  Singleton
 2 {
 3     Singleton()
 4      {
 5     }

 6
 7      public   static  Singleton Instance
 8      {
 9          get
10          {
11              return  Nested.instance;
12         }

13     }

14     
15      class  Nested
16      {
17          static  Nested()
18          {
19         }

20
21          internal   static   readonly  Singleton instance  =   new  Singleton();
22     }

23 }

24

这里,初始化工作有Nested类的一个静态成员来完成,这样就实现了延迟初始化,并具有很多的优势,是值得推荐的一种实

现方式。

实现要点

l        Sigleton模式是限制而不是改进类的创建。

l         Sigleton类中的实例构造器可以设置为Protected以允许子类派生。

l         Sigleton模式一般不要支持Icloneable接口,因为这可能导致多个对象实例,与Sigleton模式的初衷违背。

l         Sigleton模式一般不要支持序列化,这也有可能导致多个对象实例,这也与Sigleton模式的初衷违背。

l         Sigleton只考虑了对象创建的管理,没有考虑到销毁的管理,就支持垃圾回收的平台和对象的开销来讲,我们一般没必要对其销毁进行特殊的管理。

l         理解和扩展Sigleton模式的核心是“如何控制用户使用new对一个类的构造器的任意调用”。

l         可以很简单的修改一个 Sigleton ,使它有少数几个实例,这样做是允许的而且是有意义的

优点

l         实例控制: Singleton 会阻止其他对象实例化其自己的 Singleton对象的副本,从而确保所有对象都访问唯一实例

l         灵活性:因为类控制了实例化过程,所以类可以更加灵活修改实例化过程

缺点

l         开销:虽然数量很少, 但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题,上面的五种实现方式中已经说过了。

l          可能的开发混淆: 使用 singleton 对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new 关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。

l         对象的生存期: Singleton 不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于 .NET Framework 的语言),只有 Singleton类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除
对象实例,但这样会导致 Singleton类中出现悬浮引用。

适用性

l         当类只能有一个实例而且客户可以从一个众所周知的访问点访问它时。

l         当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需更改代码就能使用一个扩展的实例时。

应用场景

l         每台计算机可以有若干个打印机,但只能有一个 Printer Spooler ,避免两个打印作业同时输出到打印机。
(摘自吕震宇的
C# 设计模式(7 )-Singleton Pattern

l         PC 机中可能有几个串口,但只能有一个 COM1 口的实例。

l         系统中只能有一个窗口管理器。

l         .NET Remoting 中服务器激活对象中的 Sigleton 对象,确保所有的客户程序的请求都只有一个实例来处理。

完整示例

这是一个简单的计数器例子,四个线程同时进行计数。

 1 using  System;
 2 using  System.Threading;
 3
 4 namespace  SigletonPattern.SigletonCounter
 5 {
 6      /**/ ///   <summary>
 7      ///  功能:简单计数器的单件模式
 8      ///  编写:Terrylee
 9      ///  日期:2005年12月06日
10      ///   </summary>

11      public   class  CountSigleton
12      {
13          /**/ /// 存储唯一的实例
14          static  CountSigleton uniCounter  =   new  CountSigleton();  
15    
16          /**/ /// 存储计数值
17          private   int  totNum  =   0 ;  
18    
19          private  CountSigleton() 
20    
21         
22              /**/ /// 线程延迟2000毫秒
23             Thread.Sleep( 2000 );
24         }
 
25    
26          static   public  CountSigleton Instance() 
27    
28         
29    
30              return  uniCounter; 
31    
32         }
 
33         
34          /**/ /// 计数加1
35          public   void  Add()
36         
37             totNum  ++ ;
38         }
  
39         
40          /**/ /// 获得当前计数值
41          public   int  GetCounter()
42         
43              return  totNum;
44         }
 
45
46     }

47 }

48

 1 using  System;
 2 using  System.Threading;
 3 using  System.Text;
 4
 5 namespace  SigletonPattern.SigletonCounter
 6 {
 7      /**/ ///   <summary>
 8      ///  功能:创建一个多线程计数的类
 9      ///  编写:Terrylee
10      ///  日期:2005年12月06日
11      ///   </summary>

12      public   class  CountMutilThread
13      {
14          public  CountMutilThread()
15          {
16             
17         }

18
19          /**/ ///   <summary>
20          ///  线程工作
21          ///   </summary>

22          public   static   void  DoSomeWork()
23          {
24              /**/ /// 构造显示字符串
25              string  results  =   "" ;
26
27              /**/ /// 创建一个Sigleton实例
28    CountSigleton MyCounter  =  CountSigleton.Instance();
29
30              /**/ /// 循环调用四次
31              for ( int  i = 1 ;i < 5 ;i ++ )
32              {
33         /**/ /// 开始计数
34                 MyCounter.Add();
35                 
36                 results  += " 线程 " ;
37                 results  +=  i.ToString()  +   " ——〉 " ;
38                 results  +=   " 当前的计数: " ;
39                 results  +=  MyCounter.GetCounter().ToString();
40                 results  +=   " \n " ;
41
42                 Console.WriteLine(results);
43                 
44                  /**/ /// 清空显示字符串
45          results  =   "" ;
46             }

47         }

48
49          public   void  StartMain()
50          {
51
52             Thread thread0  =  Thread.CurrentThread; 
53    
54             thread0.Name  =   " Thread 0 "
55    
56             Thread thread1  = new  Thread( new  ThreadStart(DoSomeWork)); 
57    
58             thread1.Name  =   " Thread 1 "
59    
60             Thread thread2  = new  Thread( new  ThreadStart(DoSomeWork)); 
61    
62             thread2.Name  =   " Thread 2 "
63    
64             Thread thread3  = new  Thread( new  ThreadStart(DoSomeWork)); 
65    
66             thread3.Name  =   " Thread 3 "
67    
68             thread1.Start(); 
69    
70             thread2.Start(); 
71    
72             thread3.Start(); 
73             
74              /**/ /// 线程0也只执行和其他线程相同的工作
75             DoSomeWork(); 
76         }

77     }

78 }

79

 1 using  System;
 2 using  System.Text;
 3 using  System.Threading;
 4
 5 namespace  SigletonPattern.SigletonCounter
 6 {
 7      /**/ ///   <summary>
 8      ///  功能:实现多线程计数器的客户端
 9      ///  编写:Terrylee
10      ///  日期:2005年12月06日
11      ///   </summary>

12      public   class  CountClient
13      {
14          public   static   void  Main( string [] args)
15          {
16        CountMutilThread cmt  =   new  CountMutilThread();
17
18             cmt.StartMain();
19
20             Console.ReadLine();
21         }

22     }

23 }

24

总结

Sigleton 设计模式是一个非常有用的机制,可用于在面向对象的应用程序中提供单个访问点。文中通过五种实现方式的比较和一个完整的示例,完成了对 Sigleton 模式的一个总结和探索。用一句广告词来概括 Sigleton 模式就是“简约而不简单”。

源码下载:/Files/Terrylee/SigletonPattern.rar


 

一、 单例(Singleton)模式

单例模式的特点:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其它对象提供这一实例。

单例模式应用:

  • 每台计算机可以有若干个打印机,但只能有一个Printer Spooler,避免两个打印作业同时输出到打印机。
  • 一个具有自动编号主键的表可以有多个用户同时使用,但数据库中只能有一个地方分配下一个主键编号。否则会出现主键重复。


二、 Singleton模式的结构:

Singleton模式包含的角色只有一个,就是Singleton。Singleton拥有一个私有构造函数,确保用户无法通过new直接实例它。除此之外,该模式中包含一个静态私有成员变量instance与静态公有方法Instance()。Instance方法负责检验并实例化自己,然后存储在静态成员变量中,以确保只有一个实例被创建。(关于线程问题以及C#所特有的Singleton将在后面详细论述)。


三、 程序举例:

该程序演示了Singleton的结构,本身不具有任何实际价值。

//  Singleton pattern -- Structural example  
using  System;

//  "Singleton"
class  Singleton
{
  
//  Fields
   private   static  Singleton instance;

  
//  Constructor
   protected  Singleton()  {}

  
//  Methods
   public   static  Singleton Instance()
  
{
    
//  Uses "Lazy initialization"
     if ( instance  ==   null  )
      instance 
=   new  Singleton();

    
return  instance;
  }

}


///   <summary>
///  Client test
///   </summary>

public   class  Client
{
  
public   static   void  Main()
  
{
    
//  Constructor is protected -- cannot use new
    Singleton s1  =  Singleton.Instance();
    Singleton s2 
=  Singleton.Instance();

    
if ( s1  ==  s2 )
      Console.WriteLine( 
" The same instance "  );
  }

}



四、 在什么情形下使用单例模式:

使用Singleton模式有一个必要条件:在一个系统要求一个类只有一个实例时才应当使用单例模式。反过来,如果一个类可以有几个实例共存,就不要使用单例模式。

注意:

不要使用单例模式存取全局变量。这违背了单例模式的用意,最好放到对应类的静态成员中。

不要将数据库连接做成单例,因为一个系统可能会与数据库有多个连接,并且在有连接池的情况下,应当尽可能及时释放连接。Singleton模式由于使用静态成员存储类实例,所以可能会造成资源无法及时释放,带来问题。


五、 Singleton模式在实际系统中的实现

下面这段Singleton代码演示了负载均衡对象。在负载均衡模型中,有多台服务器可提供服务,任务分配器随机挑选一台服务器提供服务,以确保任务均衡(实际情况比这个复杂的多)。这里,任务分配实例只能有一个,负责挑选服务器并分配任务。

//  Singleton pattern -- Real World example  

using  System;
using  System.Collections;
using  System.Threading;

//  "Singleton"
class  LoadBalancer
{
  
//  Fields
   private   static  LoadBalancer balancer;
  
private  ArrayList servers  =   new  ArrayList();
  
private  Random random  =   new  Random();

  
//  Constructors (protected)
   protected  LoadBalancer()
  
{
    
//  List of available servers
    servers.Add(  " ServerI "  );
    servers.Add( 
" ServerII "  );
    servers.Add( 
" ServerIII "  );
    servers.Add( 
" ServerIV "  );
    servers.Add( 
" ServerV "  );
  }


  
//  Methods
   public   static  LoadBalancer GetLoadBalancer()
  
{
    
//  Support multithreaded applications through
    
//  "Double checked locking" pattern which avoids
    
//  locking every time the method is invoked
     if ( balancer  ==   null  )
    
{
      
//  Only one thread can obtain a mutex
      Mutex mutex  =   new  Mutex();
      mutex.WaitOne();

      
if ( balancer  ==   null  )
        balancer 
=   new  LoadBalancer();

      mutex.Close();
    }

    
return  balancer;
  }


  
//  Properties
   public   string  Server
  
{
    
get
    
{
      
//  Simple, but effective random load balancer
       int  r  =  random.Next( servers.Count );
      
return  servers[ r ].ToString();
    }

  }

}


///   <summary>
///  SingletonApp test
///   </summary>
///

public   class  SingletonApp
{
  
public   static   void  Main(  string [] args )
  
{
    LoadBalancer b1 
=  LoadBalancer.GetLoadBalancer();
    LoadBalancer b2 
=  LoadBalancer.GetLoadBalancer();
    LoadBalancer b3 
=  LoadBalancer.GetLoadBalancer();
    LoadBalancer b4 
=  LoadBalancer.GetLoadBalancer();

    
//  Same instance?
     if ( (b1  ==  b2)  &&  (b2  ==  b3)  &&  (b3  ==  b4) )
      Console.WriteLine( 
" Same instance "  );

    
//  Do the load balancing
    Console.WriteLine( b1.Server );
    Console.WriteLine( b2.Server );
    Console.WriteLine( b3.Server );
    Console.WriteLine( b4.Server );
  }

}



六、 C#中的Singleton模式

C#的独特语言特性决定了C#拥有实现Singleton模式的独特方法。这里不再赘述原因,给出几个结果:

方法一:

下面是利用.NET Framework平台优势实现Singleton模式的代码:

sealed   class  Singleton
{
   
private  Singleton();
   
public   static   readonly  Singleton Instance = new  Singleton();
}

这使得代码减少了许多,同时也解决了线程问题带来的性能上损失。那么它又是怎样工作的呢?

注意到,Singleton类被声明为sealed,以此保证它自己不会被继承,其次没有了Instance的方法,将原来_instance成员变量变成public readonly,并在声明时被初始化。通过这些改变,我们确实得到了Singleton的模式,原因是在JIT的处理过程中,如果类中的static属性被任何方法使用时,.NET Framework将对这个属性进行初始化,于是在初始化Instance属性的同时Singleton类实例得以创建和装载。而私有的构造函数和readonly(只读)保证了Singleton不会被再次实例化,这正是Singleton设计模式的意图。
(摘自:http://www.cnblogs.com/huqingyu/archive/2004/07/09/22721.aspx

不过这也带来了一些问题,比如无法继承,实例在程序一运行就被初始化,无法实现延迟初始化等。

详细情况可以参考微软MSDN文章:《Exploring the Singleton Design Pattern》

方法二:

既然方法一存在问题,我们还有其它办法。

public   sealed   class  Singleton
{
  Singleton()
  
{
  }


  
public   static  Singleton GetInstance()
  
{
    
return  Nested.instance;
  }

    
  
class  Nested
  
{
    
//  Explicit static constructor to tell C# compiler
    
//  not to mark type as beforefieldinit
     static  Nested()
    
{
    }


    
internal   static   readonly  Singleton instance  =   new  Singleton();
  }

}

这实现了延迟初始化,并具有很多优势,当然也存在一些缺点。详细内容请访问:《Implementing the Singleton Pattern in C#》。文章包含五种Singleton实现,就模式、线程、效率、延迟初始化等很多方面进行了详细论述。

posted on 2006-07-16 23:12 SoRoMan 阅读(1510) 评论(3)  编辑 收藏 引用

评论

# 哇~小鸡好厉害^_^崇拜ing~re: Design Pattern之Singleton模式 2006-07-17 10:25 Catherine
小鸡辛苦了~
俺好好看!  回复  更多评论
  

# 小鸡面试成功~~re: Design Pattern之Singleton模式 2006-07-19 10:04 Catherine
今天小鸡去9you面试~
预祝成功^_^  回复  更多评论
  

# re: Design Pattern之Singleton模式 2007-12-30 08:52 sdgsdg
http://eros-amatoriale-gratuito.gradis-tun.info
http://video-xxx-89-com.sculaccia-tun.info
http://maschio-nudo-coito-anale.sculaccia-tun.info
http://cerco-donna-sesso-pulito.gradis-tun.info
http://gay-hairy-men.gradis-tun.info
http://farm-beast-porno-zoo-gratis-clip.sculaccia-tun.info
http://incontro-sesso-tre.fatte-tun.info
http://matura-daily-gallery.gradis-tun.info
http://trans-riceve-a-roma.sculaccia-tun.info
http://foto-sesso-anale-cazzo-grosso-nero.gradis-tun.info
http://filmati-maialone-freeware.gradis-tun.info
http://grande-obese-figa.fatte-tun.info
http://foto-fatish.fatte-tun.info
http://super-sexi-hard-moovie.sculaccia-tun.info
http://anteprime-gratis-donna-porca.sculaccia-tun.info
http://forum-sito-porno-gratis.gradis-tun.info
http://posizione-deel-sesso.sculaccia-tun.info
http://sesso-rio-de-janeiro.fatte-tun.info
http://hot-girl-xxx.fatte-tun.info
http://veb-cam-gÄarl.gradis-tun.info
http://anteprime-gratis-film-porno.sculaccia-tun.info
http://sesso-orale-lesbo.gradis-tun.info
http://foto-video-porno-asiatica-gratis.sculaccia-tun.info
http://figa-vecchia-casalinga-gratis.fatte-tun.info
http://marco-passera-basket.gradis-tun.info
http://giovane-ragazza-fanno-sesso.sculaccia-tun.info
http://sesso-al-telefono-solo-ascolto.sculaccia-tun.info  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理