zoukankan      html  css  js  c++  java
  • 对Singleton的实现方法做一个总结

    原帖:

    http://www.cnblogs.com/ASPNET2008/archive/2008/05/09/1190328.html

    首先直接文章作者及回复中的兄弟提到的两种Singleton方法。

    第一种

        public class Singleton
        
    {
            
    private static Singleton _instance = null;
            
    private static readonly object lockHelper = new object();
            
    private Singleton()
            
    {
            }

            
    public static Singleton CreateInstance()
            
    {
                
    //这样lock以及lock块内的代码只会在第一次调用CreateInstance方法的时候执行,
                
    //第一次调用该方法后_instance就不再为null了,if块内的代码就无须执行了
                if (_instance == null)
                
    {
                    
    lock (lockHelper)
                    
    {
                        
    if (_instance == null)
                            _instance 
    = new Singleton();
                    }

                }

                
    return _instance;
            }

        }

    第二种

    public class Singleton
        
    {
            
    private static readonly Singleton _instance = new Singleton();

            
    static Singleton() { }

            
    private Singleton() { }

            
    public static Singleton CreateInstance() return _instance; }
        }

    先说说以上两种方法。 

    第一种方法, 显而易见的, 性能不佳; 而且最好加上一句Thread.MemoryBarrier()。 这是为了线性化内存存取, 避免处理器调整指令顺序导致问题, 至于到底怎么个坏掉法, 我就糊里糊涂了。 但至少从这个角度看, 其实第一种方法, 虽然我们最常用, 如果不加这么一句, 可能反而是不安全的。

     第二种方法, 有兄弟说它不是线程安全的, 事实上它是线程安全的, 但不一定是AppDomain安全的。 在同一个AppDomain内, 初始化仅进行一次。但是这种方法有一个细节。 对于没有静态构造函数的类, .NET会加上一个BeforeFieldInit的标志,它的MSDN解释是:Specifies that calling static methods of the type does not force the system to initialize the type.  这就是说, 假设Singleton存在一个静态方法, 比如Singleton.Do(), 这个Do被执行了, 但是_instance并不一定被初始化(当然也不一定不被),只要没有其它条件触发(在后面我们会想一个办法, 保证一定得到这个LazyLoad的效果); 也就是说, 初始化的时间是不固定的; 而且还有别的可能:  比如在程序集被加载, 在该类被某种方式访问, 甚至一个很随机的时刻。 传说中初始化仅仅保证在该字段被访问之前, 由运行时决定, 到底咋回事就不是我关心的范畴了。

    其实早晚本来也是无所谓的, 关键是注意到这一点, 可以避免并没有触发初始化, 可是流程上人脑中有一个预期的行为,进行编程时所造成的不确定性(尽管很难想象能够掉进这个陷阱的场景,估计得特别特殊的应用, 比如你脑海里以为初始化了, 就向其它东西发一个信号, 而且会造成问题等等)。

    如果存在静态构造函数, 那么当这个类的任意成员被访问前, 会进行初始化。 微软这么设计显然是有道理的: 既然写了静态构造器, 那么编程者很显然希望它最早执行, 同时也就会捎带上初始化。 但这种方式会导致, 如果咱们访问Singleton.Do(), 那么所有的字段都会被初始化。也许我们调用Do()的时候因为用不着这个实例, 而且这个实例会耗费很多资源,于是就想尽量Lazy Load用不着就不new, 这样要么使用第一种方法, 或者引出了第三个方法。

        public class Singleton
        
    {
            
    private Singleton() { }

            
    public static Singleton CreateInstance() return Holder._instance; }

            
    private class Holder
            
    {
                
    static Holder()
                
    {
                }


                
    public static readonly Singleton _instance = new Singleton();
            }

        }

    用内部私有的类Holder玩一个花招, 实际上这是一种最好的办法: 当Holder被访问时, new Singleton(), 而Do()的时候, 则不会这么做。这样, 它既有第一种方法LazyLoad的特性, 又具有第二种方法的直接性和性能, 还避免了乱七八糟的很难搞明白的问题。

    回头再说说关于第一种方法的另一个替代品, 这样:

    public class Singleton
    {
                
    public static Singleton _instance = null;

                
    private Singleton()
                
    {

                }


                
    public static Singleton CreateInstance()
                
    {
                    Interlocked.CompareExchange
    <Singleton>(ref _instance, new Singleton(), null);
                    
    return _instance;
                }

    }


    因为Interlocked.CompareExchange是一个原子操作, 所以是线程安全的。不过注意那个new Singleton(), 它会不断的被执行。 如果构造的过程很耗费资源, 这是一个问题。 其实利用delegate, 我们可以有一些新玩法, 把包括第二次及以后进入CreateInstance的开销降到最低; 如果谁想到了可以给我留言。(Update: 我的做法放在了第20楼, 注意脑袋回复中提到的问题, 其实这种做法我一般用在别处完成更贴切的任务, 具体分析日后再说了)

    最后, 稍加改动, 可以把Singleton成泛型的。不过个人感觉意义不大了, 除非用Singleton的地方太多, 懒得打代码, 也可以考虑。 嗯, 结束了么? 不知道大家觉得这些是不是也一塌糊涂,自己感觉我不太适合写这类文章....
  • 相关阅读:
    DNS隧道
    记录上锁(fcntl)
    posix对线程的调整
    MySQL创建存储过程
    MySQL的WHERE语句中BETWEEN与IN的用法和他们的区别
    mysql中distinct
    线程的工作方式-流水线
    可执行程序的生成过程
    线程高级编程
    time函数及其用法
  • 原文地址:https://www.cnblogs.com/guaiguai/p/1190803.html
Copyright © 2011-2022 走看看