美文网首页设计模式程序员
C#实现设计模式 —— 单例模式

C#实现设计模式 —— 单例模式

作者: ChainZhang | 来源:发表于2017-12-28 17:01 被阅读77次

    本文为转载,原文:C#实现设计模式 —— 单例模式

    介绍

    作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。

    特点

    • 单例类只能有一个实例。
    • 单例类必须自己创建自己的唯一实例。
    • 单例类必须给所有其他对象提供这一实例。

    单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。

    单例模式的实现

    实现单例模式有两种方案,饿汉式和懒汉式。

    饿汉式

    public class EagerSingleton
    {
        private EagerSingleton() { }
        private static EagerSingleton instance = new EagerSingleton();
    
        public static EagerSingleton GetInstance()
        {
            return instance;
        }
    
        public void Print()
        {
            Console.WriteLine("this is eager singleton");
        }
    }
    

    在这个类被加载时,静态变量instance会被初始化,此时类的私有构造子会被调用。这时候,单例类的唯一实例就被创建出来了。

    饿汉式其实是一种比较形象的称谓。既然饿,那么在创建对象实例的时候就比较着急,饿了嘛,于是在装载类的时候就创建对象实例。

    饿汉式是典型的空间换时间,当类装载的时候就会创建类的实例,不管你用不用,先创建出来,然后每次调用的时候,就不需要再判断,节省了运行时间。

    懒汉式

    public class LazySingleton
    {
        private LazySingleton() { }
        private static LazySingleton _instance;
        public static LazySingleton Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new LazySingleton();
                }
                return _instance;
            }
        }
    
        public void Print()
        {
            Console.WriteLine("this is lazy singleton");
        }
    }
    

    上面的单例模式的实现在单线程下确实是完美的,然而在多线程的情况下会得到多个Singleton实例,因为在两个线程同时运行时,此时两个线程判断(_instance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使new LazySingleton方法在同一时间只运行一个线程运行就好了,也就是我们线程同步的问题了(对于线程同步大家也可以参考我线程同步的文章),具体的解决多线程的代码如下:

    public class LazySingleton
    {
        private LazySingleton() { }
        private static LazySingleton _instance;
        private static readonly object locker = new object();
        public static LazySingleton Instance
        {
            get
            {
                lock (locker)
                {
                    if (_instance == null)
                    {
                        _instance = new LazySingleton();
                    }
                }
                return _instance;
            }
        }
    
        public void Print()
        {
            Console.WriteLine("this is lazy singleton");
        }
    }
    

    上面这种解决方案确实可以解决多线程的问题,但是上面代码对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在,对于这个操作完全没有必要的,因为当第一个线程创建了该类的实例之后,后面的线程此时只需要直接判断(_instance==null)为假,此时完全没必要对线程辅助对象加锁之后再去判断,所以上面的实现方式增加了额外的开销,损失了性能,为了改进上面实现方式的缺陷,我们只需要在lock语句前面加一句(_instance==null)的判断就可以避免锁所增加的额外开销,这种实现方式我们就叫它 “双重锁定”,下面具体看看实现代码的:

    public class LazySingleton
    {
        private LazySingleton() { }
        private static LazySingleton _instance;
        private static readonly object locker = new object();
        public static LazySingleton Instance
        {
            get
            {
                if (_instance == null)
                {
    
    
                    lock (locker)
                    {
                        if (_instance == null)
                        {
                            _instance = new LazySingleton();
                        }
                    }
                }
                return _instance;
            }
        }
    
        public void Print()
        {
            Console.WriteLine("this is lazy singleton");
        }
    }
    

    饿汉式和懒汉式区别

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

    1. 线程安全:
      饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
      懒汉式本身是非线程安全的,为了实现线程安全需要加锁。

    2. 资源加载和性能:
      饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
      而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    测试代码

    static void Main(string[] args)
    {
        bool flag = true;
        while (flag)
        {
            Console.WriteLine("=================设计模式测试=================");
            Console.WriteLine("====1 - 简单工厂模式==========================");
            Console.WriteLine("====2 - 工厂方法模式==========================");
            Console.WriteLine("====3 - 抽象工厂模式==========================");
            Console.WriteLine("====4 - 单例模式==============================");
            Console.WriteLine("====0 - 退出==================================");
            Console.WriteLine("==============================================");
            Console.Write("请输入编号:");
            string num = Console.ReadLine();
            switch (num)
            {
                case "1":
                    SimpleFactory();
                    break;
                case "2":
                    FactoryMethod();
                    break;
                case "3":
                    AbstractFactory();
                    break;
                case "4":
                    Singleton();
                    break;
                case "0":
                    flag = false;
                    break;
                default:
                    break;
            }
    
            Console.WriteLine("\r\n");
        }
    }
    
    static void Singleton()
    {
        EagerSingleton.GetInstance().Print();
        LazySingleton.Instance.Print();
    }
    
    运行结果

    源码地址

    转载请注明出处:
    C#实现设计模式 —— 单例模式

    相关文章

      网友评论

        本文标题:C#实现设计模式 —— 单例模式

        本文链接:https://www.haomeiwen.com/subject/hmuvgxtx.html