C#单例

单 例模式是广为流传的设计模式中的一种。本质上,单例模式是一个只允许创建一个实例,并提供对这个实例简单的访问途径的类。一般而言,单例模式在创建实例时 不允许传递任何参数-否则不同参数导致不同的实例创建,就会出现问题!(如果同一个实例可以被同参的不同请求所访问,那么工厂模式会更适合。)这篇文章只 针对无参创建的请求进行讨论。典型的,单例模式的应用往往是延后创建的(created lazily)---只有在第一次被用到的时候才会被创建。在C#中有实现单例模式有很多种方法。我将在这里一一展现给大家,从最常见的、线程不安全的到 延后创建的、线程安全的、再到简洁高效的版本。注意在下面的代码中,我忽略了所有私有域,因为私有域是默认的类的成员。In many other languages such as Java, there is a different default, and private should be used.

所有的这些实现都有以下四个特征:

1.只有一个构造函数,而且是私有的,不带参数的。这是为了防止其他类对其实例化(这和模式本身有冲突)。同时也防止了子类化--如果一个单例能被子类化 一次,就能被子类化两次,而如果每个子类可以创建一个实例,这与模式本身又产生了冲突。如果你(遇到这样的情况):只有在运行期才能知道实际的类型,因此 你需要一个父类的单例,你可以使用工厂模式。

2.类是密封的。这并不是必须的,严格的说,即如上一点所说的原因,可以提高JIT(Just-In-Time , 运行时编译执行的技术)的效率。

3.一个静态变量用来保存单例的引用。

4.一个用以访问单例引用的公用静态方法。

注意:所有这些实现都用到一个公用静态属性Instance,作为访问实例的方法。当然都可以替换为方法,这对线程安全和性能都没有影响。

版本1-非线程安全

// Bad code! Do not use!
public sealed class Singleton
{
static Singleton instance=null;
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}


如 上提示,上面的代码是非线程安全的。两个线程可能同时判断“if (instance==null)”,发现为TRUE,于是都创建了实例,这又违背了单例模式。注意:事实上在表达式反应前实例已经被创建了,内存模型并 不保证新的实例的值被其他的线程看到,除非对应的内存屏障已经通过了。(CPU越过内存屏障后,将刷新自已对存储器的缓冲状态,这样其他线程才能同步自己 的copy)

版本2 - 简单的线程安全

public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}
}

这 个实现是线程安全的。线程首先对共用的对象进行锁定,然后判断实例是否在之前已经创建。这里要小心内存屏障问题(在锁定的时候确保所有的读操作发生在所获 得之后,在解锁的时候确保所有的写操作发生在锁释放之前),确保只有一个线程创建了实例(因为在同一时刻只有一个线程可以在执行那段代码--到了第二个线 程进入的时候,第一个线程已经完成了实例的创建,这样表达式返回false)。不幸的是,由于在每次访问的时候都要进行锁定,所以影响了性能。(这对于多 线程并发的高性能要求的应用显得尤为重要)。注意有些该版本的实现对typeof(Singleton)进行锁定,但我是对类的私有静态变量进行锁定。对 那些可被其他类访问的对象进行锁定或对类型进行锁定会导致性能问题甚至引起死锁。这是一个地雷-任何地方都有可能发生,只有对本就为锁定而创建的对象进行 锁定或是那些因为某些目的而被锁定的文档才是安全的。通常这些对象都是私有的。这样有助于写出线程安全的程序。

版本3 -- 用双重检测机制的线程安全

// Bad code! Do not use!
public sealed class Singleton
{
static Singleton instance=null;
static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
if (instance==null)
{
lock (padlock)
{
if (instance==null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
}

 

最近在学设计模式,学到创建型模式的时候,碰到单例模式(或叫单件模式),现在整理一下笔记。

  在《Design Patterns:Elements of Resuable Object-Oriented Software》中的定义是:Ensure a class only has one instance,and provide a global point of access to。它的主要特点不是根据客户程序调用生成一个新的实例,而是控制某个类型的实例数量-唯一一个。(《设计模式-基于C#的工程化实现及扩展》,王翔)。也就是说,单例模式就是保证在整个应用程序的生命周期中,在任何时刻,被指定的类只有一个实例,并为客户程序提供一个获取该实例的全局访问点。

  一、经典模式:

public class Singleton
{
private static Singleton instance;

private Singleton()
{

}

public static Singleton GetInstance()
{
if(instance==null)
{
instance=new Singleton();
}
return instance;
}
}


  解析如下:

  1)首先,该Singleton的构造函数必须是私有的,以保证客户程序不会通过new()操作产生一个实例,达到实现单例的目的;

  2)因为静态变量的生命周期跟整个应用程序的生命周期是一样的,所以可以定义一个私有的静态全局变量instance来保存该类的唯一实例;

  3)必须提供一个全局函数访问获得该实例,并且在该函数提供控制实例数量的功能,即通过if语句判断instance是否已被实例化,如果没有则可以同new()创建一个实例;否则,直接向客户返回一个实例。

  在这种经典模式下,没有考虑线程并发获取实例问题,即可能出现两个线程同时获取instance实例,且此时其为null时,就会出现两个线程分别创建了instance,违反了单例规则。因此,需对上面代码修改。

  二、多线程下的单例模式

  1、Lazy模式

public class Singleton
{
private static Singleton instance;
private static object _lock=new object();

private Singleton()
{

}

public static Singleton GetInstance()
{
if(instance==null)
{
lock(_lock)
{
if(instance==null)
{
instance=new Singleton();
}
}
}
return instance;
}
}


  上述代码使用了双重锁方式较好地解决了多线程下的单例模式实现。先看内层的if语句块,使用这个语句块时,先进行加锁操作,保证只有一个线程可以访问该语句块,进而保证只创建了一个实例。再看外层的if语句块,这使得每个线程欲获取实例时不必每次都得加锁,因为只有实例为空时(即需要创建一个实例),才需加锁创建,若果已存在一个实例,就直接返回该实例,节省了性能开销。

  2、饿汉模式

  这种模式的特点是自己主动实例。

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

private Singleton()
{
}

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

  上面使用的readonly关键可以跟static一起使用,用于指定该常量是类别级的,它的初始化交由静态构造函数实现,并可以在运行时编译。在这种模式下,无需自己解决线程安全性问题,CLR会给我们解决。由此可以看到这个类被加载时,会自动实例化这个类,而不用在第一次调用GetInstance()后才实例化出唯一的单例对象。

 

最牛逼的解决方案:(晚实现Lazy)

晚实现:当使用的才去new


 public sealed class A
    {

        private A()
        {

        }

        public static A getA()
        {
            return B.a;
        }

        class B
        {
            public static A a = new A();
        }

    }

 

 

posted @ 2013-05-22 22:14  法的空间  阅读(519)  评论(0编辑  收藏  举报