C# 单例模式整理

参考:


方式1. 非线程安全

 

public class Singleton
{
    private static Singleton instance = null;

    private Singleton() { }

    public static Singleton GetInstance()
    {
        if (instance == null)
        {
            instance = new Singleton();
        }

        return instance;
    }
}

可能有两个线程都执行了 'if (instance==null)',且得到的结果都是 true,然后两个线程各自创建一个实例。

(有可能在执行 'if (instance==null)' 前,实例就已经创建好了,但是 memory model 不能保证其它线程能及时发现 instance 的值已经改变。(内存栅栏,memory barrier))

 

 

方式2. 简单的线程安全

 

public sealed class Singleton
{
    private static readonly object s_lock = new object();

    private static Singleton instance = null;

    private Singleton() { }

    public static Singleton GetInstance()
    {
        lock (s_lock)
        {
            if (instance == null)
            {
                instance = new Singleton();
            }

            return instance;
        }
    }
}

lock 可以消除内存栅栏的影响。

 

在CLR 中,任何锁方法的调用都构成了一个完整的内存栅栏,在栅栏之前写入的任何变量都必须在栅栏之前完成;在栅栏之后的任何变量读取都必须在栅栏之后开始。


缺点:每次获取 instance 都得拿锁,性能会降低。

 


方式3. 用双检锁实现线程安全

 

public class Singleton
{
    private static object s_lock = new object();

    private static Singleton instance = null;

    private Singleton() { }

    public Singleton GetInstance()
    {
        if (instance == null)
        {
            Monitor.Enter(s_lock);

            if (instance == null)
            {
                Singleton temp = new Singleton();
                Volatile.Write(ref instance, temp);
            }
        }

        return instance;
    }
}

> 在 Java 中不可靠。JVM 在 GetInstance 方法开始时将 instance 的值读入 CPU 寄存器。对第二个 if 语句求值时,直接查询寄存器,造成第二个 if 语句总是得到 true。

 

> 为什么用 Volatile.Write 而不是 instance = new Singleton() ?

如果用 instance = new Singleton(),编译器可能先为 Singleton 分配内存,将引用赋给 instance,再调用构造器。这样可能在调用构造器完成之前,另一个线程调用了 GetInstance 并使用这个未构造完成的 Singleton 对象。
Volatile.Write 保证 temp 中的引用只有在构造器结束后才赋给 instance。

 


方式4. 不用锁的线程安全、非延迟创建

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

    private Singleton() { }

    public static Singleton GetInstance()
    {
        return instance;
    }
}

缺点:首次访问类的任何成员都会调用类型构造器,从而创建实例。

 

 


方式5. 延迟创建

 

public class Singleton
{
    private Singleton() { }

    public static Singleton GetInstance()
    {
        return Nested.instance;
    }

    private class Nested
    {
        internal static readonly Singleton instance = new Singleton();
    }
}



方式6. 用 Lazy<T>

public class Singleton
{
    private static Lazy<Singleton> instance
        = new Lazy<Singleton>(() => new Singleton(), true);

    private Singleton() { }

    public Singleton GetInstance()
    {
        return instance;
    }
}

 

 

 

如果不知道选哪个,就用方式6。

你可能感兴趣的:(Singleton)