[设计模式]-SingleTon(单例模式)
1.单例的意图
1.1 单例的意义就在于实现了单例模式的类只存在一份实例,无法通过new来创建其他的实例,这样可以达到节省资源以及约束用户实例化的行为。
1.2 单例的应用场景可以是一些全局公共的类,比如1. 应用程序的日志类,2. 配置文件的加载和读取类,3. 缓存容器类等等。
2.应该注意的问题
2.1 SingleTon类不能实现ICloneable接口,如果这样做的话会很矛盾
2.2 不能序列化与反序列化,因为反序列化之后的实例为一个新的实例
2.3 构造函数的参数问题
2.4 对象销毁和垃圾回收问题
3.实现代码及相关说明
/****************************************************************************** * Author: wjn * Create: 2012/5/6 23:02:27 * Title: [设计模式]-SingleTon(单例模式) * Description: 关于SingleTon模式的一些记录 * CopyRight: wjn2010.cnblogs.com * Reference: http://csharpindepth.com/Articles/General/Singleton.aspx * http://www.cnblogs.com/TomXu/archive/2011/12/19/2291448.html * Notes: Codes based on .Net Framework 4.0 ******************************************************************************/ using System; using System.Linq; using System.Reflection; using System.Threading.Tasks; namespace SingleTonDemo { /// <summary> /// 单线程版本(单例的原型,线程不安全) /// </summary> sealed class SingleTon { private static SingleTon instance; /// <summary> /// 私有构造函数,让外部无法通过new来构造 /// </summary> private SingleTon(){} /// <summary> /// 内部构造供外部访问 /// </summary> public static SingleTon Instance { get { return instance ?? (instance = new SingleTon()); } } } /// <summary> /// 线程安全的版本(double check) /// 这里的线程安全是指单例对象本身是线程安全的,但是单例的成员(变量)不是线程安全的,所有的单例版本都有这个问题, /// 如果需要成员也时线程安全的话,最简单的方法就是加锁。 /// </summary> sealed class SingleTon { /// <summary> /// 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制, /// 详细可以参考链接 http://msdn.microsoft.com/zh-cn/library/x13ttww7.aspx" /// </summary> private volatile static SingleTon instance; /// <summary> /// 互斥体 /// </summary> private static readonly object mutex=new object(); private SingleTon(){} public static SingleTon Instance { get { if (instance == null) { lock (mutex) { if (instance == null) { instance = new SingleTon(); } } } return instance; } } } /// <summary> /// CLR下版本,C#编译器会在访问静态字段之前调用静态构造函数给字段赋值, /// 并且即使是多线程环境,也会只有一个线程调用静态构造函数,之后的线程便不会再调用。 /// 反编译IL即可看到一个名为beforefieldinit指令 /// </summary> sealed class SingleTon { public static readonly SingleTon Instance = new SingleTon(); private SingleTon() { } } /// <summary> /// 延迟加载版本,通过内部类来构造实例 /// </summary> sealed class SingleTon { private SingleTon(){} public static SingleTon Instance { get { return Nested.InnerInstance; } } private static class Nested { static Nested(){} internal static readonly SingleTon InnerInstance = new SingleTon(); } } /// <summary> /// 通过Lazy类来构造延迟加载且线程安全的版本 /// 关于Lazy可以参考http://msdn.microsoft.com/en-us/library/dd997286.aspx /// </summary> sealed class SingleTon { private SingleTon() { } private static readonly Lazy<SingleTon> lazy = new Lazy<SingleTon>(() => new SingleTon()); public static SingleTon Instance { get { return lazy.Value; } } } /// <summary> /// 泛型版本(处理了构造函数问题) /// </summary> /// <typeparam name="T"></typeparam> public abstract class Singleton<T> { private static readonly Lazy<T> instance = new Lazy<T>(() => { var ctors = typeof(T).GetConstructors( BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); if (ctors.Count() != 1) throw new InvalidOperationException(String.Format("Type {0} must have exactly one constructor.", typeof(T))); var ctor = ctors.SingleOrDefault(c => !c.GetParameters().Any() && c.IsPrivate); if (ctor == null) throw new InvalidOperationException(String.Format("The constructor for {0} must be private and take no parameters.", typeof(T))); return (T)ctor.Invoke(null); }); public static T Instance { get { return instance.Value; } } } /// <summary> /// 演示单例成员的线程不安全问题 /// </summary> class SingleTon:Singleton<SingleTon> { static readonly object mutex=new object(); public int Counter { get; private set; } private SingleTon(){} public void Increment() { lock (mutex) { Counter++; } } } /// <summary> /// 测试类 /// </summary> class Program { static void Main(string[] args) { Parallel.For(0, 1000, result=> { for (var i = 0; i < 1000; i++) { SingleTon.Instance.Increment(); } }); //如果没有在Increment()上加锁,那么这个值一般会小于1000*1000 Console.WriteLine(SingleTon.Instance.Counter); Console.ReadKey(); } } }
4.扩展
单例是一种设计模式,因此不能停留在概念之上,相对一个实例来说,单例体现的是一种对实例构造的控制权,使用户无法随意构造实例,避免过多的浪费和不必要的问题,所以通过单例模式我们应该看到的是一种对对象管理的模式,单例也可以扩展为多例,构建为一个对象池。事实上目前.Net Framework中的线程池或者是连接池,就是把固定数目的对象放到堆栈或者队列等数据结构中来维护,已到达更好的利用这些稀缺资源的目的。
作者:wjn
出处:http://wjn2010.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利
出处:http://wjn2010.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利