文章目录

  • 简介
  • 不安全线程的单例模式
  • 简单安全线程带锁
  • 双重检查 - 带锁
  • 安全初始化
  • 安全并且懒汉式静态初始化
  • 带泛型的懒汉式单例
  • 异常
  • 提高效率
  • 总结

简介

  单例模式是软件工程中广为人知的设计模式。单例模式就是指一个永远只能实例化一次。使用的方式是调用类里创建的静态方法。通常来说,单例模式创建的类,都是不带形参的 ,原因就是当创建多个实例的时候,如果参数不同的话(比如2个不同的重载构造函数),那么就会造成一些不必要的问题(如果相同的实例要被创建而且他们使用相同的参数的话,那么建议使用工厂模式),这篇文章的定位就是没有 任何的参数的情况下,通常情况下,单例模式是LAZY的,也就是说相当的容易创建。

  在C#中实现单例模式有很多种方式。我将在下面以上面的目录的形式呈现给大家,开始我会跟大家介绍最常用的单例模式的写法,这些写法的线程并不安全,然后会提到懒汉式写法(Lazy-Load),然后就是线程安全,最后会跟大家介绍一下提高效率的方式。

  所有的实现将会用通俗的语言来介绍,但是要注意以下:

  • 只有一个构造函数,而且这个构造函数是私有的,不带任何的参数的。这是为了防止其他的类实例化这个单例类(这也许违反了设计模式),注意这种方式也会阻止子类,如果单例能被子类实例化一次,那么也可以被子类实例化多次。如果单例的每个子类都可以创建实例的话,就违反了单例模式。如果你需要父类(基类)的单例的实现的话我觉得工厂模式是可以有用到的,但是具体的类型不会被知道直到运行时(CLR)开始。
  • 类是sealed的,也就是封闭的。这是没有必要的,严格意义上说,只是为了JIT提高效率。
  • 静态变量 ,类型被定义为这个单例类的一个引用。
  • 建立一个公共的而且静态的方法,返回值是这个单例类,如果有需要的话。

  注意所有的实现都是用一个公共的且静态的属性作为实例的入口。在所有的情况下属性可以方便的转换成方法,而且和线程安全或者效率不冲突。

第一个版本 -  非线程安全

// 不要用这种方式
public sealed class Singleton
{
private static Singleton instance=null; private Singleton()
{
} public static Singleton Instance
{
get
{
if (instance==null)
{
instance = new Singleton();
}
return instance;
}
}
}

  我要表明的是,上面的方法是非线程安全的,2个不同的线程可以同时进入这个方法,如果instance为空的并且这里返回真的情况下,都可以创建实例,这显然违反了单例模式,实际上,在测试以前,实例就已经有可能被创建了,但是内存模型不能保证这个实例能被其他的线程看到,除非合适的内存屏障已经被跨过了。

第二个版本 - 简单安全线程

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

  上述实现是线程安全的。这个线程在共享的object上取出了一把锁,然后在创建实例以前检查这个实例是否被创建了。这个保护了内存屏障问题(lock保证了所有的读取操作是在LOCK获得以后发生的,所有的unlock保证了所有的写操作在lock 释放以后发生的),这样就保证了一个线程只能创建一个实例(每次只有一个线程在这段代码中运行),不巧的是,性能上来说,锁变成了每次都必须的当这个实例被响应的时候。

  注意除了在锁当中锁住typeof(Singleton)这种类型的以外,我锁住了一个静态私有的变量,对于这个类来说。如果是锁 的一个对象的话,其他的类可以进入并且锁住(比如Type)这样会造成性能风险的问题甚至死锁。这是我的大体偏好 - 也许可能的话,只有锁住对象才能达到最终的目的,或者某些文章说锁是为了达到一些特别的目的。(比如等待或者脉冲一个队列)。通常来说这样的对象应该被设置成私有的。这样会让写线程安全更加的容易。

第三个版本 - 尝试线程安全(双重锁定)

public sealed class Singleton
{
private static Singleton instance = null;
private 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;
}
}
}

  这个实现尝试了线程安全,当然并没必要每次都要取出lock,但是这种方式有如下4个缺点:

  • 这种方式JAVA中是无效的。也许你会认为我这是在说废话,但是我觉得这是值得你去了解的。JAVA内存模型中新对象的引用被分配到实例之前并不能保证构造函数完成初始化,JAVA 内存模型重新工作(在1.5版本中),但是双重检查锁依然是坏的,在不带volatile 的变量(比如C#)。
  • 不带任何的记忆屏障,在ECMA CLI规格中也是破碎的。也许在.NET 2.0以下是安全的,但是我更倾向于不依赖于哪些强类型的语义,特别的说,对于安全性来说这是值得怀疑的。如果把变量变成 volatile的是可以运行的,明确的内存屏障会进行响应,虽然后面有些情况专家都不能完全同意屏障是必须的。我打算尝试避免站在对的或者错误的立场上去回答这个问题!
  • 容易报错。这种模式要像上面一样的精确 - 任何的大的改动都会造成正确性和性能方面的冲击。
  • 依然没有后面说的那种写法好。

第四个版本 -  不完全lazy,但是线程安全且不用用锁

public sealed class Singleton
{
private static readonly Singleton instance = new Singleton(); // 显示的static 构造函数
//没必要标记类型 - 在field初始化以前
static Singleton()
{
} private Singleton()
{
} public static Singleton Instance
{
get
{
return instance;
}
}
}

未完待续,原文链接地址:

http://csharpindepth.com/Articles/General/Singleton.aspx#unsafe

C# 中实现单例模式的更多相关文章

  1. Objective-C中的单例模式

    ​    ​单例模式算是设计模式中比较简单的一种吧,设计模式不是只针对某种编程语言,在C++, Java, PHP等其他OOP语言也有设计模式,笔者初接触设计模式是通过<漫谈设计模式>了解 ...

  2. 转:C++中的单例模式

    C++中的单例模式 单例模式也称为单件模式.单子模式,可能是使用最广泛的设计模式.其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享.有很多地方需要这样的功能模块, ...

  3. 浅谈iOS中的单例模式

    iOS中的单例模式     就我本身理解而言,我认为的单例:单例在整个工程中,就相当于一个全局变量,就是不论在哪里需要用到这个类的实例变量,都可以通过单例方法来取得,而且一旦你创建了一个单例类,不论你 ...

  4. 关于JDK中采用单例模式的类

    JDK设计模式应用——单例模式(Singleton) <JDK源码分析>的分支,讲解设计模式在jdk中使用. 我们从三个方面讲述,一是:jdk源码中的设计模式:二是:讲解设计模式(UML图 ...

  5. java中的单例模式与doublecheck

    转自: http://devbean.blog.51cto.com/448512/203501 在GoF的23种设计模式中,单例模式是比较简单的一种.然而,有时候越是简单的东西越容易出现问题.下面就单 ...

  6. java中的单例模式与静态类

    单例模式与静态类(一个类,所有方法为静态方法)是另一个非常有趣的问题,在<Java中有关单例模式的面试问题>博文中露掉了,由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例 ...

  7. 【译】在C#中实现单例模式

    目录 介绍 第一个版本 --不是线程安全的 第二个版本 -- 简单的线程安全 第三个版本 - 使用双重检查锁定尝试线程安全 第四个版本 - 不太懒,不使用锁且线程安全 第五版 - 完全懒惰的实例化 第 ...

  8. JavaScript中的单例模式

    单例模式 在JavaScript中,单例(Singleton)模式是最基本又最有用的模式之一.这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一的变量进行访问.确保单例 ...

  9. Python学习笔记之在Python中实现单例模式

    有些时候你的项目中难免需要一些全局唯一的对象,这些对象大多是一些工具性的东西,在Python中实现单例模式并不是什么难事.以下总结几种方法: 使用类装饰器 使用装饰器实现单例类的时候,类本身并不知道自 ...

随机推荐

  1. Red Hat5.5 install Generic mysql-5.7.10

    1.确认以下依赖包已安装 [ncurses ncurses-devel openssl-devel bison autoconf automake bison gcc m4 libtool make ...

  2. 对 griview获取的数据添加方法 6月

    <asp:TemplateField HeaderText="日期">                            <HeaderStyle CssCl ...

  3. XCod5 SVN

    近日开始学习IOS开发, 涉及版本管理问题,老大说要使用SVN, 在Windows系统中使用SVN的经验使得俺以为需要首先安装SVN,然后再配置等等, 殊不知XCode5中已经内置了SVN, 在百度一 ...

  4. 用jdbc访问大段文本数据

    package it.cast.jdbc; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.F ...

  5. ABP理论学习之日志记录

    返回总目录 本篇目录 服务端 获取Logger 基类中的Logger 配置 客户端 服务端 ABP使用的是Castle Windsor的日志记录设备.它可以和不同的日志类库一起工作,比如Log4Net ...

  6. ABP理论学习之MVC视图

    返回总目录 本篇目录 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc Nuget包集成了MVC视图.因此你可以像常规那样创建MVC视图. AbpWebViewPage基 ...

  7. Python黑帽编程 3.1 ARP欺骗

    Python灰帽编程 3.1 ARP欺骗 ARP欺骗是一种在局域网中常用的攻击手段,目的是让局域网中指定的(或全部)的目标机器的数据包都通过攻击者主机进行转发,是实现中间人攻击的常用手段,从而实现数据 ...

  8. Spring:源码解读Spring IOC原理

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

  9. 用Powershell启用Windows Azure上的远程桌面服务

    [题外话] 某天不小心点了XX管家的自动修复,虽然及时点了取消也看到了远程桌面服务成功被关闭,但是忙完该干的事以后竟然忘记了这件事,在断开远程桌面服务之前也忘记再次打开.以至于之后几天一直以为Azur ...

  10. Leetcode 刷题计划

    Two Sum    21.4%    Medium Given an array of integers, return indices of the two numbers such that t ...