C# 设计模式之单例模式

总目录


前言

本文是个人基于C#学习设计模式总结的学习笔记,希望对你有用!


1 基本介绍

定义:确保一个类只有一个实例,并提供一个全局访问点。

本质就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个。

2 适用场景

单例模式通常适用于在整个应用程序中只需要一个实例化对象的场景,以确保资源的高效利用和应用程序的稳定性。

  • 日志系统:在应用程序中,通常只需要一个日志系统,以避免在多个地方创建多个日志对象。这一般是由于共享的日志文件一直处于打开状态,所以只能有一个实例去操作,否则内容不好追加也有可能造成资源占用加剧资源消耗。
  • 数据库连接池:在应用程序中,数据库连接池是一个非常重要的资源,单例模式可以确保在应用程序中只有一个数据库连接池实例,避免资源浪费。主要是节省打开或者关闭数据库连接所引起的效率损耗,因为何用单例模式来维护,就可以大大降低这种损耗。
  • 配置文件管理器:在应用程序中,通常只需要一个配置文件管理器来管理应用程序的配置文件,单例模式可以确保在整个应用程序中只有一个配置文件管理器实例。这个是由于配置文件是共享的资源。
  • 缓存系统:在应用程序中,缓存系统是一个重要的组件,单例模式可以确保在整个应用程序中只有一个缓存实例,以提高应用程序的性能。
  • 网站在线人数统计:其实就是全局计数器,也就是说所有用户在相同的时刻获取到的在线人数数量都是一致的。
  • GUI组件:在图形用户界面(GUI)开发中,单例模式可以确保在整个应用程序中只有一个GUI组件实例,以确保用户界面的一致性和稳定性。

3 实现方式

  • 懒汉式:类加载不会导致该单实例对象被创建,而是首次使用该对象时才会创建。
  • 饿汉式:类加载就会导致该单实例对象被创建。
  • 懒汉式,公有静态方法,非线程安全
csharp 复制代码
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class Singleton
    {
        //定义一个静态变量用于保存实例
        private static Singleton instance;

        private Singleton()
        {
			//将构造函数私有化,使外界不能创建该类实例
        }

        //定义公有静态方法,提供一个全局访问点(也可以定义公有属性作为全局访问点)
        public static Singleton GetInstance()
        {
            if (instance == null)
            {
                instance = new Singleton();
            }
            return instance;
        }
    }

在GetInstance方法中,我们检查instance是否为null,如果是,则创建一个新的Singleton实例并将其赋值给instance。最后,我们返回instance实例。这样,无论何时调用Instance属性,它都将返回同一个Singleton实例。通过这种方式,我们实现了单例模式,确保了整个应用程序中只有一个Singleton实例存在,并提供了一个全局访问点来访问该实例。

  • 懒汉式,公有属性,非线程安全
csharp 复制代码
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class Singleton
    {
        //定义一个静态变量用于保存实例
        private static Singleton _instance;

        //定义公有静态属性,提供一个全局访问点
        public static Singleton Instance
        {
            get 
            {
                if (_instance == null)
                {
                    _instance = new Singleton();
                }
                return _instance;
            }
            set { _instance = value; }
        }

        private Singleton()
        {
            //将构造函数私有化,使外界不能创建该类实例
        }
    }

此种方法同上面的方法是一样的作用,只不过调用的时候,一个是使用方法,一个是使用属性而已。

  • 懒汉式,公有属性,线程安全

以上的代码实现了在单线程下是OK的,然而在多线程的情况下会得到多个Singleton 实例,因为在两个线程同时运行GetInstance方法时,此时两个线程判断(_instance ==null)这个条件时都返回真,此时两个线程就都会创建Singleton的实例,这样就违背了我们单例模式初衷了,既然上面的实现会运行多个线程执行,那我们对于多线程的解决方案自然就是使GetInstance方法在同一时间只运行一个线程运行就好了

csharp 复制代码
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class Singleton
    {
        //定义一个静态变量用于保存实例
        private static Singleton _instance;

        //定义一个线程锁
        private static readonly object _instanceLock = new object();

        //定义公有静态属性,提供一个全局访问点
        public static Singleton Instance
        {
            get 
            {
                lock (_instanceLock)
                {
                    if (_instance == null)
                    {
                        _instance = new Singleton();
                    }
                }
                return _instance;
            }
            set { _instance = value; }
        }

        private Singleton()
        {
            //将构造函数私有化,使外界不能创建该类实例
        }
    }
  • 懒汉式,公有属性,双重锁定,线程安全

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

csharp 复制代码
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class Singleton
    {
        //定义一个静态变量用于保存实例
        private static Singleton _instance;

        //定义一个线程锁
        private static readonly object _instanceLock = new object();

        //定义公有静态属性,提供一个全局访问点
        public static Singleton Instance
        {
            get 
            {
                if (_instance==null)
                {
                    lock (_instanceLock)
                    {
                        if (_instance == null)
                        {
                            _instance = new Singleton();
                        }
                    }
                }
                return _instance;
            }
            set { _instance = value; }
        }

        private Singleton()
        {
            //将构造函数私有化,使外界不能创建该类实例
        }
    }
  • 饿汉式
csharp 复制代码
    /// <summary>
    /// 单例模式的实现
    /// </summary>
    public class Singleton
    {
        //定义一个私有静态的只读的全局变量
        private static readonly Singleton instance = new Singleton();


        private Singleton()
        {
            //将构造函数私有化,使外界不能创建该类实例
        }

        //设置为静态方法则可在外边无需创建该类的实例就可调用该方法
        public static Singleton GetInstance()
        {
            return instance;
        }
    }

在c#中使用静态初始化时无需显示地编写线程安全代码,C# 与 CLR 会自动解决前面提到的懒汉式单例类时出现的多线程同步问题。

当整个类被加载的时候,就会自行初始化 instance 这个静态只读变量。

而非在第一次调用 GetInstance()时再来实例化单例类的唯一实例,所以这就是一种饿汉式的单例类


总结

以上就是今天要讲的内容,本文介绍了单例模式的使用,而希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料
C#设计模式(1)------单例模式
C#中单例模式详解

相关推荐
△曉風殘月〆3 小时前
WPF MVVM入门系列教程(二、依赖属性)
c#·wpf·mvvm
逐·風4 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#
m0_656974747 小时前
C#中的集合类及其使用
开发语言·c#
九鼎科技-Leo8 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
捕鲸叉8 小时前
MVC(Model-View-Controller)模式概述
开发语言·c++·设计模式
wrx繁星点点8 小时前
享元模式:高效管理共享对象的设计模式
java·开发语言·spring·设计模式·maven·intellij-idea·享元模式
凉辰8 小时前
设计模式 策略模式 场景Vue (技术提升)
vue.js·设计模式·策略模式
菜菜-plus8 小时前
java设计模式之策略模式
java·设计模式·策略模式
暗黑起源喵8 小时前
设计模式-迭代器
设计模式
lexusv8ls600h10 小时前
微服务设计模式 - 网关路由模式(Gateway Routing Pattern)
spring boot·微服务·设计模式