[设计模式]-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中的线程池或者是连接池,就是把固定数目的对象放到堆栈或者队列等数据结构中来维护,已到达更好的利用这些稀缺资源的目的。

posted @ 2012-05-07 00:06  wjn2010  阅读(247)  评论(0编辑  收藏  举报