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中实现单例模式并不是什么难事.以下总结几种方法: 使用类装饰器 使用装饰器实现单例类的时候,类本身并不知道自 ...
随机推荐
- siteserver cms选择栏目搜索无效
标签必须以空格分开,且option 的value必须给id不能给名称
- STL之关联容器
关联容器包含map.set.multimap.multiset. 关联容器的特点是明显的,相对于顺序容器,有如下特点: 1.其内部是采用非线性的二叉树结构,具体的说是红黑树的结构原理实现的. 2.se ...
- Android课程---序列化与反序列化(转)
ava序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?本文围绕这些问题进行了探讨. 1.Java序列化与反序列化 Java序列化是指把Java对象转换为字节序列的 ...
- HTML实现简单计算器
<!DOCTYPE html> <html> <meta name="content-type" content="text/html; c ...
- 学习笔记: Delphi之线程类TThread
新的公司接手的第一份工作就是一个多线程计算的小系统.也幸亏最近对线程有了一些学习,这次一接手就起到了作用.但是在实际的开发过程中还是发现了许多的问题,比如挂起与终止的概念都没有弄明白,导致浪费许多的时 ...
- NEsper Nuget包
Esper是专门进行复杂事件处理(CEP)的流处理平台,Java版本为Esper,.Net版本为NEsper.Esper & NEsper可以方便开发者快速开发部署处理大容量消息和事件的应用系 ...
- 用"hosting.json"配置ASP.NET Core站点的Hosting环境
通常我们在 Prgram.cs 中使用硬编码的方式配置 ASP.NET Core 站点的 Hosting 环境,最常用的就是 .UseUrls() . public class Program { p ...
- [PHP源码阅读]strlen函数
文章来自:http://www.hoohack.me/2016/02/22/phps-source-analytics-strlen 我在github有对PHP源码更详细的注解.感兴趣的可以围观一下, ...
- [.net 面向对象程序设计进阶] (26) 团队开发利器(五)分布式版本控制系统Git——图形化Git客户端工具TortoiseGit
[.net 面向对象程序设计进阶] (26) 团队开发利器(五)分布式版本控制系统Git——图形化Git客户端工具TortoiseGit 读前必备: 接上篇: 分布式版本控制系统Git——使用GitS ...
- C++的性能C#的产能?! - .Net Native 系列向导
之前一文<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATIVE初窥> 获得很多朋友支持和鼓励,也更让我坚定做这项技术的推广者,希望能让更多的朋友了解这项技术,于是先从官方 ...