C#设计模式之Singleton模式

前言

Singleton是二十三个设计模式中比较重要也比较经常使用的模式。但是这个模式虽然简单,实现起来也会有一些小坑,让我们一起来看看吧!

实现思路

首先我们看看这个设计模式的UML类图。

很清晰的可以看到,有三点是需要我们在实现这个模式的时候注意的地方。

  • 私有化的构造器
  • 全局唯一的静态实例
  • 能够返回全局唯一静态实例的静态方法

其中,私有化构造器是防止外部用户创建新的实例而静态方法用于返回全局唯一的静态实例供用户使用。原理清楚了,接下来我们看看一些典型的实现方式和其中的暗坑。

实现方法

最简单的实现方法

最简单的实现方法自然就是按照UML类图直接写一个类,我们看看代码。

  class Program
  {
    static void Main(string[] args)
    {
    	var single1 = Singleton.Instance;
      var single2 = Singleton.Instance;
      Console.WriteLine(object.ReferenceEquals(single1, single2));
      Console.ReadLine();
    }
  }

  class Singleton
  {
    private static Singleton _Instance = null;
    private Singleton()
    {
      Console.WriteLine("Created");
    }

    public static Singleton Instance
    {
      get
      {
        if (_Instance == null)
        {
          _Instance = new Singleton();
        }
        return _Instance;
      }
    }

    public void DumbMethod()
    {

    }
  }

这段代码忠实的实现了UML类图里面的一切,查看输出结果,

证实了Singleton确实起了作用,多次调用仅仅产生了一个实例,似乎这么写就可以实现这个模式了。但是,真的会那么简单吗?

如果多线程乱入?

现在我们给刚刚的例子加点调料,假设多个对实例的调用,并不是简单的,彬彬有礼的顺序关系,二是以多线程的方式调用,那么刚刚那种实现方法,还能从容应对吗?让我们试试。把Main函数里面的调用改成这样。

	static void Main(string[] args)
    {
      int TOTAL = 10000;
      Task[] tasks = new Task[TOTAL];
      for (int i = 0; i < TOTAL; i++)
      {
        tasks[i] = Task.Factory.StartNew(() =>
        {
          Singleton.Instance.DumbMethod();
        });
      }
			Task.WaitAll(tasks);
      Console.ReadLine();
    }

通过Factory创造出1万个Task,几乎同时去请求这个单例,看看输出。

咦,我们刚刚写的Singleton模式失效了,这个类被创造了5次(这段代码运行多次,这个数字不一定相同),一定是多线程搞的鬼,我们刚刚写的代码没有办法应对多线程,换句话说,是非线程安全的(thread-safe),那有没有办法来攻克这个难关呢?

线程安全的单例模式

Lock版本

提到线程安全,很多同学第一反应就是用lock,不错,lock是个可行的办法,让我们试试。添加一个引用类型的对象作为lock对象,修改代码如下(什么?你问我为什必须是引用类型的对象而不能是值类型的对象?因为lock的时候,如果对象是值类型,那么lock仅仅锁住了它的一个副本,另外一个线程可以畅通无阻的再次lock,这样lock就失去了阻塞线程的意义)

	private static object _SyncObj = new object();
    public static Singleton Instance
    {
      get
      {
        lock (_SyncObj)
        {
          if (_Instance == null)
          {
            _Instance = new Singleton();
          }
          return _Instance;
        }
      }
    }

运行一下,输出

只有一个实例创建,证明Lock起作用了,这个模式可行!不过有些不喜欢用Lock的同学可能要问,还有没有其他办法呢?答案是有的。

静态构造器版本

回想一下,C#中的类静态构造器,只会在这个类第一次被使用的时候调用一次,天然的线程安全,那我们试试不用Lock使用类静态构造器?修改Singleton类如下:

  class Singleton
  {
    private static Singleton _Instance = null;
    private Singleton()
    {
      Console.WriteLine("Created");
    }

    static Singleton()
    {
      _Instance = new Singleton();
    }

    //private static object _SyncObj = new object();
    public static Singleton Instance
    {
      get { return _Instance; }
    }

    public void DumbMethod()
    {

    }
  }

去掉了Lock,添加了一个类静态构造器,试一试。

完美!对于不喜欢用Lock(在这个例子中,实例只会创建一次但是之后的所有线程都要先排队Lock再进入Critical code进行检查,效率比较低下)的同学,类静态构造器提供了一种很好的选择。
不过俗话说,人心苦不足 , 我们总是追求卓越。这个版本比Lock版本似乎更好一点,那还有没有更好的版本呢?有的。

Lazy版本

从net 4.0开始,C#开始支持延迟初始化,通过Lazy关键字,我们可以声明某个对象为仅仅当第一次使用的时候,再初始化,如果一直没有调用,那就不初始化,省去了一部分不必要的开销,提升了效率。如果你不熟悉Lazy或者想更多了解它,请参考。我们今天关注的重点在于,Lazy也是天生线程安全的,所以我们尝试用它来实现Singleton模式?修改代码如下:

  class Singleton
  {
    private static Lazy<Singleton> _Instance = new Lazy<Singleton>(() => new Singleton());
    private Singleton()
    {
      Console.WriteLine("Created");
    }

    public static Singleton Instance
    {
      get
      {
        return _Instance.Value;
      }
    }

    public void DumbMethod()
    {

    }
  }

输出结果中可以看到,我们达到了想要的效果:

在上面的代码中,私有变量_Instance现在是被声明为延迟初始化,这样不但天然实现了线程安全,同时在没有调用Instance静态方法的时候(也即没有调用_Instance.Value),初始化不会发生,这样就提高了效率。

总结

Singleton模式很常见,实现起来也很简单,只是要小心线程安全。以上三种方法都可以实现线程安全的Singleton模式。如果net 版本在4.0之上,建议使用Lazy版本,毕竟对比Lock版本,Lazy版本可以免去实现手动Lock之苦,对比Static版本,又有延迟初始化的性能优势,何乐而不为呢?

以上就是C#设计模式之Singleton模式的详细内容,更多关于C#中的Singleton模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • c#单例模式(Singleton)的6种实现

    1.1.1 摘要 在我们日常的工作中经常需要在应用程序中保持一个唯一的实例,如:IO处理,数据库操作等,由于这些对象都要占用重要的系统资源,所以我们必须限制这些实例的创建或始终使用一个公用的实例,这就是我们今天要介绍的--单例模式(Singleton). 使用频率高 单件模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全局访问点. 1.1.2 正文 图1单例模式(Singleton)结构图 单例模式(Singleton)是几个创建模式中最对立的一个,它的主要特点不是根据用户

  • C#单例模式(Singleton Pattern)详解

    (新手写博客,主要是对自己学习的归纳总结.会对很多小细节详解.) 单例模式的定义: 确保一个类只有一个实例,并提供一个全局访问点. 首先实例大家应该都明白就是类生成对象的过程简单的就是String s=new String(),则s就是个实例. Q:如何只生成一个实例? A:1)首先必须将构造函数变为私有从而防止其他类实例化,并且只能有一个构造函数.因为系统会默认一个无参构造函数,而且默认public访问修饰符. 所以必须写一个私有无参让默认无效.(通常单例模式都是不带形参的) 2)在该类中声明

  • C#单例模式(Singleton Pattern)实例教程

    本文以实例形式讲述了C#单例模式(Singleton Pattern)的实现方法,分享给大家供大家参考.具体实现方法如下: 一般来说,当从应用程序全局的角度来看,如果只允许类的一个实例产生,就可以考虑单例模式. 1.即时加载的单例模式 把类的实例赋值给类的一个静态字段. class Program { static void Main(string[] args) { Logger log = Logger.GetInstance(); log.WriteToFile(); Console.Re

  • C#多线程Singleton(单件)模式模板

    复制代码 代码如下: private static volatile T _instance = null; private static object objLock = new Object(); private T() { } public static T Instance { get { if (_instance == null) { lock (objLock) { if (_instance == null) { _instance = new T(); } } } return

  • C#设计模式之Singleton模式

    前言 Singleton是二十三个设计模式中比较重要也比较经常使用的模式.但是这个模式虽然简单,实现起来也会有一些小坑,让我们一起来看看吧! 实现思路 首先我们看看这个设计模式的UML类图. 很清晰的可以看到,有三点是需要我们在实现这个模式的时候注意的地方. 私有化的构造器 全局唯一的静态实例 能够返回全局唯一静态实例的静态方法 其中,私有化构造器是防止外部用户创建新的实例而静态方法用于返回全局唯一的静态实例供用户使用.原理清楚了,接下来我们看看一些典型的实现方式和其中的暗坑. 实现方法 最简单

  • Java设计模式之单态模式(Singleton模式)介绍

    定义:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作. 还有,singleton能够被状态化:这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到. 另外方面,Singleton也能够

  • JavaScript设计模式之工厂模式和构造器模式

    什么是模式 前阵子准备期末考试,劳神又伤身的,实在闲不得空来更新文章,今天和大家说说javascript中的设计模式. 首先呢,我们需要知道的是:模式是一种可复用的解决方案,而反模式呢就是针对某个问题的不良解决方案. js反模式常见例子 1.向setTimeout和setInterval传递字符串,而不是函数,这会触发eval()的内部使用. 2.在全局上下文中定义大量的变量污染全局命名空间 3.修改Object类的原型 4.以内联形式使用js,嵌入在HTML文件中的js代码是无法包含在外部单元

  • JS设计模式之访问者模式定义与用法分析

    本文实例讲述了JS设计模式之访问者模式定义与用法.分享给大家供大家参考,具体如下: 在访问者模式中,主要包括下面几个角色 1.抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的. 2.访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情. 3.抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的.抽象元素一般有两类方法,一部分是本身的业务逻辑,另外

  • Android设计模式之代理模式Proxy浅显易懂的详细说明

    一.概述 代理模式也是平时比较常用的设计模式之一,代理模式其实就是提供了一个新的对象,实现了对真实对象的操作,或成为真实对象的替身.在日常生活中也是很常见的.例如A要租房,为了省麻烦A会去找中介,中介会替代A去筛选房子,A坐享中介筛选的结果,并且交房租也是交给中介,这就是一个典型的日常生活中代理模式的应用.平时打开网页,最先开到的一般都是文字,而图片等一些大的资源都会延迟加载,这里也是使用了代理模式. 代理模式的组成 Abstract Subject:抽象主题-声明真实主题和代理主题共同的接口

  • Java设计模式单例模式(Singleton)用法解析

    这篇文章主要介绍了Java设计模式单例模式(Singleton)用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 单例模式的应用场景: 单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例.并提供一个全局反访问点.单例模式是创建型模式.单例模式在生活中应用也很广泛,比如公司CEO只有一个,部门经理只有一个等.JAVA中ServletCOntext,ServetContextCOnfig等,还有spri

  • Java设计模式之单件模式深入讲解

    目录 定义 Java单件模式 经典单件模式的实现 多线程单件模式的实现 急切创建实例 双重检查加锁 Python单件模式 模块实现 new关键字实现 装饰器实现 函数装饰器 类装饰器 定义 单件模式确保一个类只有一个实例,并提供一个全局访问点 Java单件模式 经典单件模式的实现 public class Singleton{ private static Singleton uniqueInstance; // 利用一个静态变量来记录Singleton类的唯一实例 private Single

  • .Net设计模式之原型模式(Prototype)

    一.动机(Motivation) 在软件系统中,经常面临着“某些结构复杂的对象”的创建工作:由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口.如何应对这种变化?如何向“客户程序(使用这些对象的程序)”隔离出“这些易变对象”,从而使得“依赖这些易变对象的客户程序”不随着需求改变而改变? 二.意图(Intent) 使用原型实例指定创建对象的种类,然后通过拷贝这些原型来创建新的对象——<设计模式>GoF 三.结构(Structure) 我们看了这么多设计模式的类图了,大

  • 一文带你了解Java设计模式之原型模式

    目录 定义 解决的问题 核心要点 类图 浅复制与深复制的区别 代码实现 未使用设计模式 实现Cloneable接口 深复制-重写clone 深复制-通过对象序列化实现(推荐) 拓展 定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式其实就是从一个对象在创建另外一个可定制的对象,不需要知道任何创建的细节 解决的问题 在运行期建立和删除原型. 经常用于: 类初始化消耗资源较多 构造函数比较复杂 核心要点 1.实现cloneable 接口,重写Object的clone方法

  • JavaScript编程设计模式之构造器模式实例分析

    本文实例讲述了JavaScript编程设计模式之构造器模式.分享给大家供大家参考,具体如下: 经典的OOP语言中,构造器(也叫构造函数)是一个用于初始化对象的特殊方法.在JS中,因为一切皆对象,对象构造器经常被提起. 对象构造器用于建立制定类型(Class)的对象,可以接受参数用于初始化对象的属性和方法. 对象建立 在JS中,有三个常用的方法用于建立对象: //1, 推荐使用 var newObject = {}; //2, var newObject = Object.create( null

随机推荐