C#单例的几种写法

恶汉式单例(鼓励的写法

    internal class MonsterManager
    {
        private static MonsterManager instance = new MonsterManager();
        public static MonsterManager Instance
        {
            get { return instance; }
        }
    }

懒汉式单例

    internal class MonsterManager
    {
        private static MonsterManager instance;
        public static MonsterManager Instance
        {
            get
            {
                if (instance == null)
                    instance = new MonsterManager();
                return instance;
            }
        }
    }

这两个名词最近也是在一本设计模式书上看到,觉得挺有意思的,这边就借用下。这两种方式,本质上都是到第一个单例对象被引用时(也就是Instance对象被调用时),才会去创建对象。当然,有一点点区别,如果MonsterManager有另一个static方法存在并且被调用时,恶汉会去创建Instance,而懒汉会无动于衷。但我们一般设计单例对象时,不太会同时留有static方法功能,所以个人感觉这个初始化时机的差别可以忽略不计。

然而,懒汉存在一个缺点。如果在多线程的情况下,instance可能会被创建多次(两个线程都new了自己的instance),导致单例并不能成为真正的单例了。

改进的懒汉

    internal class MonsterManager
    {
        private static MonsterManager instance;
        public static MonsterManager Instance
        {
            get { return instance ?? (instance = new MonsterManager()); }
        }
    }

看上去这次是一行完成了取值和赋值的过程了。这样的写法是多线程安全的吗?答案是否定的。

IL_0000: nop
IL_0001: ldsfld class ConsoleApplication9.MonsterManager ConsoleApplication9.MonsterManager::'instance'
IL_0006: stloc.0
IL_0007: br.s IL_0009

IL_0009: ldloc.0
IL_000a: ret

上面是对应的IL代码,可以看出??操作符只是一个语法糖,本质上这种写法只是看上去变得很优雅,仍旧是线程不安全的。

线程安全的懒汉单例

    internal class MonsterManager
    {
        private static MonsterManager instance = new MonsterManager();
        private static readonly object lockRoot = new object();

        public static MonsterManager Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (lockRoot)
                    {
                        if (instance == null)
                            instance = new MonsterManager();
                    }
                }
                return instance;
            }
        }
    }

这种方法,看上去很繁琐,但实际是可用的,并且是线程安全的。需要引入一个锁变量,在new单例对象时,防止多线程的重入。这么看来,C#没有类似于JAVA的synchronized关键词,还是非常坑的。

猜你喜欢

转载自blog.csdn.net/narlon/article/details/82909465