C#设计模式——单例模式(Singleton Pattern)

目录

一、概述

1)基础

二、实现

1)单线程

2)解决——多线程情况下

解决方案一:Sleep

解决方案二:加锁

三、扩展


一、概述

单例模式->创建型设计模式

定义:保证一个类只有一个实例,并提供一个访问它的全局访问点。(在第一个使用者创建了这个类的实例之后,其后需要使用这个类就只能使用之前创建的实例,无法再创建一个新的实例。)

1)基础

        “确保一个类只有一个实例” + “提供一个访问它的全局访问点

        首先,我们再创建类的实例会想到用什么方式来创建?new。

        那么为什么可以通过它来创建一个实例?因为构造函数,若未在类中定义构造函数,则编译器会自动帮我们生成一个无参的构造函数。如果类定义私有的构造函数就不能在外界通过new创建实例了。【故,确保一个类只有一个实例,利用“定义私有构造函数”+“定义一个保存该实例的静态私有变量”(为什么定义为静态?静态是为了在多线程中确保只有一个实例)】

        其次,关于提供实例的全局访问点?可以定义一个公有方法或者属性来把该类的实例公开出去。

二、实现

1)单线程

通过索引器实现:目标实例只可存在一个

  • 实现原理:get中判断目标实例是否为空,为空则new一个,反则不进行操作,返回一个存在的实例。
  • 索引器小知识get 获取属性值, set 设置属性值。

  • public class Prpgram
    {
        static void Main(String[] args)
        {
            PrA a = PrA.Intance;    //对象1
            PrA b = PrA.Intance;    //对象2
            Console.Read();
        }
        ///实例类
        public class PrA
        {
            //定义一个PrA类型的静态变量来保存类的实例
            private static PrA intance = null;
            //定义公有属性来提供全局访问点
            public static PrA Intance
            {
                get
                {
                    if (intance is null)    //决定是否实例化对象,管理实例化的操作
                    {
                        intance = new PrA();
                    }
                    return intance;
                }
            }
            //定义私有构造函数,使外界不能创建该类实例
            private PrA()
            {
                Console.WriteLine("实例化!");
            }
        }
    }
  • C#设计模式——单例模式(Singleton Pattern)_第1张图片

     结论:单例模式下,任何地方对单例类创建对象,程序最多只存在一个实例。

        (字段命名规范,首字母大写的是给外部使用,小写的给内部使用。)

  • 注意:以上只适合单线程情况

    • 多线程情况下产生问题C#设计模式——单例模式(Singleton Pattern)_第2张图片
    • 结果:多次实例化
    • 解释

      • 打印多个?因为同一个时间片完成n个线程的输出,即同时完成对intance字段的get获取,皆为null时访问,故将访问n次实例化n个对象。
      • for循环20次却只产生小于20个的对象?线程创建需要时间,而Mian方法中代码执行速度很快,即有可能执行至程序结束时部分线程还未创建完成。

2)解决——多线程情况下

解决方案一:Sleep

  • 实现:在for中利用Sleep解决,达到线程非同时进行。
  • 存在问题:多线程之间切记不使用Sleep直接停止线程。(这里不详细介绍)

解决方案二:加锁

  • 实现对于每个线程都会对线程辅助对象locker加锁之后再判断实例是否存在
  • 优点:多线程之间,线程安全的一种做法。
  • 缺点这个版本加上了一个对instance的锁,在调用instance之前要先对objlock上锁,这样就避免了实现一中的线程冲突,该实现自始至终只会创建一个instance了。但是,由于每次调用Instance都会使用到锁,而调用锁的开销较大,这个实现会有一定的性能损失。
  • for (int i = 0; i < 20; i++)
    {
        Thread th = new Thread(x =>
        {
            PrA pa = PrA.Intance;   //创建了很多对象,很多单例模式
        });
        th.IsBackground = true;
        th.Start();
        }
        /*Thread.Sleep(100);    //方案一:可解决该方式下多线程多实例化的情况,存在问题 */
    }
    /*方案二:加锁
    ...
        private static object objlock = new object();
        public static PrA Intance
        {
            get
            {
                lock (objlock)    //加锁
                {
                    if (intance is null)    //决定是否实例化对象,管理实例化的操作
                    { 
                        intance = new PrA(); 
                    }
                    return intance;
                }
    
            }
        }
        //定义私有构造函数,使外界不能创建该类实例
        private PrA()
        {
             Console.WriteLine("实例化!");
        }
    ...
    */
  • 改进

        利用双层加锁


...
    // 双重锁定只需要一句判断就可以了
    if (intance is null)
    {
        lock (objlock)    //加锁
        {
            if (intance is null)    //实例是否存在
            { 
                intance = new PrA(); 
            }
            return intance;
        }
    }
...

三、扩展

先看以下代码 

public class Prpgram
{
    static void Main(String[] args)
    {
        List list = new List();

        for (int i = 0; i < 20; i++)
        {
            Thread th = new Thread(x =>
            {
                PrA pa = PrA.Intance;   //创建了很多对象
                list.Add(pa);
            });
            th.IsBackground = true;
            th.Start();
        }
        Thread.Sleep(500); //防止执行至程序结束时线程还未创建完成
        list[0].list_ss.Add(new ss() { name = "aa", age = 123 });   //给第一添加数据
        list[0].id = 1;
        list[6].id = 2;
        foreach (var item in list)
        {
            if (item.list_ss.Count > 0)
            {
                Console.WriteLine($"{item.list_ss[0].name}-{item.list_ss[0].age}-{item.id}");
            }
        }
        Console.Read();
    }

    ///实例类
    public class PrA
    {
        //静态PrA类型的字段
        private static PrA intance = null;
        private static object objlock = new object();
        public static PrA Intance
        {
            get
            {
                lock (objlock)
                {
                    if (intance is null)    //决定是否实例化对象,管理实例化的操作
                    { intance = new PrA(); }
                    return intance;
                }

            }
        }
        //构造函数
        private PrA()
        { Console.WriteLine("实例化!"); }

        public List list_ss = new();
        public int id = 0;
    }

    public class ss
    {
        public string name { get; set; }
        public int age { get; set; }
    }
}

C#设计模式——单例模式(Singleton Pattern)_第3张图片

借鉴大佬文章:https://www.cnblogs.com/zhili/p/SingletonPatterm.html

你可能感兴趣的:(C#设计模式,单例模式,设计模式,c#)