首页 > 代码库 > Singleton单例模式

Singleton单例模式

学习笔记
 
《设计模式》中对单件模式的介绍:
 
使用单件模式的主要意图就是控制该类只能够创建一个实例,同时向客户程序提供一个访问它的全局访问点。
 
实际上,单件模式要做的就是通过控制类型实例的创建过程,确保客户程序使用的都是创建好的同一个实例。
C#代码描述:
 
public class Singleton{    static public Singleton instance ;   // 唯一实例    protected Singleton () { }           //封闭客户程序的直接实例化    public static Singleton Instance    //全局访问点    {        get        {            if ( instance == null)                instance = new Singleton();            return instance;        }    }}
这段代码已满足经曲单件模式的设计要求,大多数情况下也可以很好地工作,但在多线程环境下,还存大很多缺陷。
在综合执行效率和线程同步的考虑后,采用一个Double Check的方式修正上面的代码如下:
C#增加Double Check后的Singleton
 
public class Singleton{    protected Singleton (){}    static volatile Singleton instance = null ;    //Lazy方式创建唯一实例的过程    public static Singleton Instance()    {         if(instance == null )               //外层 if             lock(typeof(Singleton))   //多线程中共享资源同步                 if(instance == null )     // 内层if                    instance = new Singleton();         return instance;    }}
Double Check方式有以下几处改进:
  • 外层if,避免客户程序每次执行时都要先lock住Singleton类型。每次都锁定Singleton类型会导致效率低下。
  • lock加内层if组成了一个相对线程安全的实例构造环境。
  • 一旦唯一的实例被创建之后 ,后续发起的调用都无须经过lock部份,直接在外层if判断之后就可获得既有的唯一实例引用。
  • volatile关键字,表示字段可能被多个线程修改,声明为volatile的字段不受编辑器优化限制,确保该字段在任何时间都是最新的值,
    也就是说在被lock之后,如果还没有真正完成new Singleton(),新加入的线程看到的instance都是null;
上面的Double Check方式,可以把构造过程放到静态构造函数中,但用C++来编写可能会产生多义性。用C#来写就能更加简洁。
引用MSND在线文章(Exploring the Singleton Design Pattern)方式:
 
sealed class Singleton{    Singleton() { }    public static readonly Singleton Instance = new Singleton();}

 

代码非常简洁精干,功能与Double Check方式相同,是多线程环境下C#实例单件模式的非常棒的方式。
  • 它省去了Double Check方式中的laze构造过程。由于Instance是类的公共静态成员,因些它会在类第一次被用到的时候构造出来,这样就不用把它的构造语句显式地写在静态构造函数中。
  • 这里实现构造函数被定义为私有的,所以客户程序能其子类从外部构造新的实例,只能通过公共静态成员Instance引用其唯一的实例,符合Singleton的设计意图。
  • 通过编译后的IL代码来分析,IL代码中有一个beforefieldinit修饰符,它告诉CLR这里的静态成员(Instance)只有在静态构造函数执行后才生效,
    因些即使有很多线程试图引用Instance,也需要等待静态构造函数执行并把静态成员Instance实例化之后才可以使用。
    其次,IL里有一个initonly,表示Instance一旦创建,就不能被任何线程修改,也就不用Double Check了。
在实际项目中,有很多因素会打破我们费尽心思实现的Singleton,这些隐蔽很深、一旦发生破坏性又很大的隐患可能会导致全局业务流程出现混乱。下面是两个情景:
  • 不要实现ICloneable接口或继承自其相关子类,否则客户程序可以跳过已经隐蔽起来的类构造函数。下面示例就导致非单一实例存在:
public class BaseEntity : System .ICloneable{    public object Clone()    //对当前实例进行克隆    {        return this .MemberwiseClone();    }}public class Singleton : BaseEntity{    //...}
  • 严防序列化。序列化本身会导致Singleton特性的破坏,因为序列化事实上完成了Singleton对象的复制和重现,所以不能对期望具有Singleton特性的类型声明SerializableAttribute属性。