zoukankan      html  css  js  c++  java
  • 【单例模式】Singleton pattern

    前言:有很多时候,在一个生命周期中我们只要一个对象就可以了,比如:线程池,缓存,对话框,日志,显卡驱动等等。如果造出多个实例,就会导致许多问题产生,例如:程序的行为异常、资源使用过量,或者说不一致的结果。

    public class Singleton
        {
            private static Singleton instance;
            private Object _synchronizedObj = new Object();
    
            //private constructor
            private Singleton() { }
           
    
            public Singleton GetInstance
            {
                get
                {
                    if (instance == null)
                        lock (_synchronizedObj)
                        {
                            if (instance == null)
                                instance = new Singleton();
                        }
                    return instance;
                }
            }
        }
    View Code

    这里用到了Lock, 我看过大部分代理都没有使用到这里,但是如果不用上,在有多线程的项目中就会出现问题。

    我用了两次NULL的判断,最外层说因为如果你第一次创建好了就不需要去进行LOCK,这里会有消耗。里面那一个NULL判断是因为如果你有多个线程一起来创建,都通过第一层NULL限制,进入了LOCK等待,当第一个对象创建完,放开了LOCK,第二进去以后,如果不加判断又会创建一次。

    有人和我说这样会不会更优雅,因为不用一个成员变量Object.

     1  public class Singleton
     2     {
     3         private static Singleton instance;
     4 
     5         //private constructor
     6         private Singleton() { }
     7 
     8 
     9         public Singleton GetInstance
    10         {
    11             get
    12             {
    13                 if (instance == null)
    14                     lock (typeof(Singleton)) //here
    15                     {
    16                         if (instance == null)
    17                             instance = new Singleton();
    18                     }
    19                 return instance;
    20             }
    21         }
    22     }
    View Code


    这里不得不提及 同步索引块(Sync block index),这个东西被关注的特别少,既然说学习CLR,这个东西可是叱咤风云(不过大部分的时候,你不会用而已啦).

    Sync block index 初始化和过程

      

    当CLR初始化的时候,CLR会初始化一个SyncBlock的数组,当一个线程到达Monitor.Enter方法时,该线程会检查该方法接受的参数的同步块索引,默认情况下对象的同步块索引是一 个负数
    (实际上并不是负数,我这里只是为了叙说方便),那么表明该对象并没有一个关联的同步块,CLR就会在全局的SyncBlock数组里找到一个空闲的项,然后将数组的索引赋值给该对象的同步块索
    引,当Monitor.Enter执行时,它会设置SyncBlock里的内容,标识出已经有一个线程占用了,当另外一个线 程进入时,它就会检查SyncBlock的内容,发现已经有一个线程占用了,该线程就
    会等待,当Monitor.Exit执行时,占用的线程就会释放SyncBlock,其他的线程可以进入操作了。

    注:参考博客园另一篇文章:http://www.cnblogs.com/qianyz/archive/2011/10/26/2224925.html(若要了解更多的SyncBlockIndex的用途和原理,可以看这篇)

     所以对于上面的代码:

      typeof(Singleton)返回一个Type对象,如果用这个,会返回相同TYPE对象。我们在这里用lock(typeof(Singleton))锁定了,相当于锁定返回的TYPE对象。OK,当我们在其他在用lock(typeof(Singleton))的时候,又锁了这个对象...2个地方锁了用了同一个对象,结果不该等待的地方也在等待了。 所以我们这里用了私有的Object对象来充当关联进索引块的对象,无论外面这么锁,都锁不住者里东西。

      

    最近逛博客的时候发现一篇文章说完美单例的,我不是很清楚完美不完美,不过的确要比我认知的单例性能要好写,附上代码:

      public class Singleton
        {
            private Singleton()
            {
            }
    
            private static class SingletonFactory
            {
                public static readonly Singleton Instance;
    
                static SingletonFactory()
                {
                    Instance = new Singleton();
                }
            }
    
            public static Singleton GetInstance()
            {
                return SingletonFactory.Instance;
            }
        }
    View Code


    那边文章说的说Java的单例,谈到了JVM的加载机制(JVM保证了类加载的时候是互斥的)如果CLR呢?静态类在加载包含该类的程序或命名空间时由 .NET Framework 公共语言运行库 (CLR;特指:C#语言) 自动加载,由于静态类里面不能有实例构造(这个很靠谱),我们造了一个静态构造(相对于java的static{})来初始化对象,因为在类加载的时候就会初始化这个对象(类加载是互斥的),所以我们就不用锁对象了,直接调用就可以。

        好处:这个相当于以前的版本,减少了判断和锁的机制,而且这里为什么会用静态类来做初始化工作,首先这个单例的类可能被继承,方法可能被重写,还有可能要继承一些接口,所以用内部静态类来做的话(静态类不能实现接口,不能被继承)功能非常的单利,就算Singlton被继承了,方法被重写,无论什么机制,我们初始化单例的地方已经得到控制,外部是不能随心所欲的去改变创建单例的模式,只能做一些自己要的修饰,然后再调用内部静态类获取单例子,这也可以说是一层封装。

    我和同事讨论一下这个模式,它固有自己的好处,但是缺点也很明显,我构建一个Connection对象的时候,我Disponse了,这样的话我们并不能在获得一次对象(如果获取对象
    的时候出错了,也无法再获取一次,要写主动加载,这样并没有封装好),但是这个东西如果要构建大对象的话,封装的还是不错,还要考虑一下大对象的资源问题(因为是
    static的,GC可不管你), 我一直认为,写模式,要写成我不知道这个是什么模式,但是用在我这个场景是最合适的,这样模式就贯通了,而不是生搬硬套...

     

  • 相关阅读:
    11.变分推断
    10.高斯混合模型GMM
    9.EM 算法
    8.指数族分布
    7.概率图模型(表示/推断/学习)
    6.核方法
    二分查找
    2.3 数据结构---数组(连续)
    C#开发Windows服务的基础代码
    C#与C++之间类型的对应{转}
  • 原文地址:https://www.cnblogs.com/guochenkai/p/3880520.html
Copyright © 2011-2022 走看看