C#设计模式-单例模式
一、 单例(Singleton)模式
单例模式的特点:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其它对象提供这一实例。
二、 单例(Singleton)模式应用场景
- Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
- windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
- 网站的计数器,一般也是采用单例模式实现,否则难以同步。
- 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
- Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
- 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
- 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
- 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
- HttpApplication 也是单位例的典型应用。熟悉ASP.NET(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
三、 单例(Singleton)模式的六种写法
1. 简单实现
using System; using System.Collections.Generic; using System.Text; namespace Singleton { public sealed class Singleton1 { private static Singleton1 instance = null; private Singleton1() { } public static Singleton1 Instance { get { if (instance==null) { instance = new Singleton1(); } return instance; } } } }
评注:
对于线程来说不安全
单线程中已满足要求
优点:
由于实例是在 Instance 属性方法内部创建的,因此类可以使用附加功能
直到对象要求产生一个实例才执行实例化;这种方法称为“惰性实例化”。惰性实例化避免了在应用程序启动时实例化不必要的 singleton。
2. 线程安全
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace Singleton 6 { 7 public sealed class Singleton2 8 { 9 private static Singleton2 instance = null; 10 private static readonly object padlock = new object(); 11 Singleton2() { } 12 public static Singleton2 Instance 13 { 14 get 15 { 16 lock(padlock) 17 { 18 if (instance==null) 19 { 20 instance = new Singleton2(); 21 } 22 return instance; 23 } 24 } 25 } 26 } 27 }
评注:
同一个时刻加了锁的那部分程序只有一个线程可以进入
对象实例由最先进入的那个线程创建
后来的线程在进入时(instence == null)为假,不会再去创建对象实例
增加了额外的开销,损失了性能
3. 双重锁
1 using System; 2 using System.Collections.Generic; 3 using System.Text; 4 5 namespace Singleton 6 { 7 /// <summary> 8 /// 单例模式-双重锁 9 /// </summary> 10 public sealed class Singleton3 11 { 12 //定义静态变量来保存类实例 13 private static Singleton3 instance = null; 14 //定义一个标识,确保线程同步 15 private static readonly object padlock = new object(); 16 //定义私有构造函数,使外界不能创建该实例 17 private Singleton3() { } 18 //定义公有属性提供一个全局点 19 public static Singleton3 Instance 20 { 21 // 当第一个线程运行到这里时,此时会对locker对象 "加锁", 22 // 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁 23 // lock语句运行完之后(即线程运行完之后)会对该对象"解锁" 24 // 双重锁定只需要一句判断就可以了 25 get 26 { 27 if (instance==null) 28 { 29 lock(padlock) 30 { 31 if (instance==null) 32 { 33 instance = new Singleton3(); 34 } 35 } 36 } 37 return instance; 38 } 39 } 40 } 41 }
评注:
多线程安全
线程不是每次都加锁
允许实例化延迟到第一次访问对象时发生
4. 静态初始化
using System; using System.Collections.Generic; using System.Text; namespace Singleton { public sealed class Singleton4 { private static readonly Singleton4 instance = new Singleton4(); static Singleton4() { } private Singleton4() { } public static Singleton4 Instance { get { return instance; } } } }
评注:
依赖公共语言运行库负责处理变量初始化
公共静态属性为访问实例提供了一个全局访问点
对实例化机制的控制权较少(.NET代为实现)
静态初始化是在 .NET 中实现 Singleton 的首选方法
5. 延迟初始化
using System; using System.Collections.Generic; using System.Text; namespace Singleton { public sealed class Singleton5 { private Singleton5() { } public static Singleton5 Instance { get { return Nested.instance; } } private class Nested { static Nested() { } internal static readonly Singleton5 instance = new Singleton5(); } } }
评注:
初始化工作由Nested类的一个静态成员来完成,这样就实现了延迟初始化。
6. 使用.NET4.0 Lazy<T>
using System; using System.Collections.Generic; using System.Text; namespace Singleton { public sealed class Singleton6 { private static readonly Lazy<Singleton6> lazy = new Lazy<Singleton6>(() => new Singleton6()); public static Singleton6 Instance { get { return lazy.Value; } } private Singleton6() { } } }
评注:
简单,性能好,可以通过IsValueCreated检测实例是否已经创建。