C# 中实现单例模式
文章目录
- 简介
- 不安全线程的单例模式
- 简单安全线程带锁
- 双重检查 - 带锁
- 安全初始化
- 安全并且懒汉式静态初始化
- 带泛型的懒汉式单例
- 异常
- 提高效率
- 总结
简介
单例模式是软件工程中广为人知的设计模式。单例模式就是指一个永远只能实例化一次。使用的方式是调用类里创建的静态方法。通常来说,单例模式创建的类,都是不带形参的 ,原因就是当创建多个实例的时候,如果参数不同的话(比如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# 中实现单例模式的更多相关文章
- Objective-C中的单例模式
单例模式算是设计模式中比较简单的一种吧,设计模式不是只针对某种编程语言,在C++, Java, PHP等其他OOP语言也有设计模式,笔者初接触设计模式是通过<漫谈设计模式>了解 ...
- 转:C++中的单例模式
C++中的单例模式 单例模式也称为单件模式.单子模式,可能是使用最广泛的设计模式.其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享.有很多地方需要这样的功能模块, ...
- 浅谈iOS中的单例模式
iOS中的单例模式 就我本身理解而言,我认为的单例:单例在整个工程中,就相当于一个全局变量,就是不论在哪里需要用到这个类的实例变量,都可以通过单例方法来取得,而且一旦你创建了一个单例类,不论你 ...
- 关于JDK中采用单例模式的类
JDK设计模式应用——单例模式(Singleton) <JDK源码分析>的分支,讲解设计模式在jdk中使用. 我们从三个方面讲述,一是:jdk源码中的设计模式:二是:讲解设计模式(UML图 ...
- java中的单例模式与doublecheck
转自: http://devbean.blog.51cto.com/448512/203501 在GoF的23种设计模式中,单例模式是比较简单的一种.然而,有时候越是简单的东西越容易出现问题.下面就单 ...
- java中的单例模式与静态类
单例模式与静态类(一个类,所有方法为静态方法)是另一个非常有趣的问题,在<Java中有关单例模式的面试问题>博文中露掉了,由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例 ...
- 【译】在C#中实现单例模式
目录 介绍 第一个版本 --不是线程安全的 第二个版本 -- 简单的线程安全 第三个版本 - 使用双重检查锁定尝试线程安全 第四个版本 - 不太懒,不使用锁且线程安全 第五版 - 完全懒惰的实例化 第 ...
- JavaScript中的单例模式
单例模式 在JavaScript中,单例(Singleton)模式是最基本又最有用的模式之一.这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一的变量进行访问.确保单例 ...
- Python学习笔记之在Python中实现单例模式
有些时候你的项目中难免需要一些全局唯一的对象,这些对象大多是一些工具性的东西,在Python中实现单例模式并不是什么难事.以下总结几种方法: 使用类装饰器 使用装饰器实现单例类的时候,类本身并不知道自 ...
随机推荐
- JS请求服务器并使页面跳转(转)
前段时间在项目中用到了前台框架EasyUI,各种组件都是差不多都是用js来渲染的,这样一些页面请求就必须用js代码来写. 但是js请求就不和html请求的玩法不怎么相同,比如我要向服务器发送一个请求然 ...
- 高灵活度,高适用性,高性能,轻量级的 ORM 实现
ORM(Object-Relational Mapping 对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,目的是提供易于理解的模型化数据的方法. ORM虽然有诸多好处,但是在实 ...
- Microsoft Azure Web Sites应用与实践【2】—— 通过本地IIS 远程管理Microsoft Azure Web Site
Microsoft Azure Web Sites应用与实践 系列: [1]—— 打造你的第一个Microsoft Azure Website [2]—— 通过本地IIS 远程管理Microsoft ...
- .NET组件程序设计之线程、并发管理(二)
.Net组件程序设计之线程.并发管理(二) 2.同步线程 手动同步 监视器 互斥 可等待事件 同步线程 所有的.NET组件都支持在多线程的环境中运行,可以被多个线程并发访问,如果没有线程同步,这样的后 ...
- .NET事件监听机制的局限与扩展
.NET中把“事件”看作一个基本的编程概念,并提供了非常优美的语法支持,对比如下C#和Java代码可以看出两种语言设计思想之间的差异. // C#someButton.Click += OnSomeB ...
- Three.js + HTML5 Audio API 打造3D音乐频谱,Let’s ROCK!
继续玩味之前写的音乐频谱作品,将原来在Canvas标签上的 作图利用Three.js让它通过WebGL呈现,这样就打造出了一个全立体感的频谱效果了. 项目详情及源码 项目GitHub地址:https: ...
- 測試大型資料表的 Horizontal Partitioning 水平切割
FileGroup 檔案群組 :一個「資料庫(database)」可對應一或多個 FileGroup,一個 FileGroup 可由一或多個 file (.ndf) 構成. FileGroup 可讓 ...
- C语言 · Interval · 求矩阵元素和
问题描述 这里写问题描述. 输入格式 测试数据的输入一定会满足的格式. 例:输入的第一行包含两个整数n, m,分别表示矩阵的行数和列数.接下来n行,每行m个正整数,表示输入的矩阵. 输出格式 要求用户 ...
- C语言 · 最小乘积(基本型)
问题描述 给两组数,各n个. 请调整每组数的排列顺序,使得两组数据相同下标元素对应相乘,然后相加的和最小.要求程序输出这个最小值. 例如两组数分别为:1 3 -5和-2 4 1 那么对应乘积取和的最小 ...
- JSP的基本语法
JSP的基本语法 一.JSP页面中的JAVA代码 二.JSP页面中的指令 三.JSP页面中的隐含对象(九大内置对象) 目录 一.JSP页面中的JAVA代码 JSP表达式(方便输出) JSP小脚本(完成 ...