【23种GOF设计模式】C#代码完整案例详解--单例模式

来自:CCNetCore社区,一个关注.Netcore领域的社区团队。

单例模式Singleton

单例模式Singleton 创建型设计模式

为了保证对象的唯一性,还可提高程序的性能。
单例模式有3种实现方式

每次创建时进行判断(需要双重判断)
将对象放入静态构造函数种实例
将对象放入静态字段中
注意:单例模式并不能保证多线程的安全性,一万次并发中可能会错误50次左右

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonPattern
{
   /// <summary>
   /// 1 单例模式(单线程和多线程)
   /// 2 单例模式的三种写法
   /// 3 单例模式的优缺点
   /// 4 深度探讨单例模式应用场景
   /// 
   /// 献给在座诸位小伙伴儿,点个赞!
   /// 今天用面试的形式来完成教学!(源自于老师亲身面试经历4年)
   /// 1 写一个单例模式 
   ///   单例模式:保证进程中,类型只有一个实例
   ///   a) 构造函数私有化,防止他人实例化
   ///   b) 对外提供一个获取实例的途径,公开的静态方法
   ///   c) 返回共用一个静态字段
   ///   60分---70分---80分---90分
   ///   怎么高分?
   ///   尽量展示自己擅长的,会几种就多写几种
   ///   面试时间就那么长,多花点没错
   ///   
   /// (面试是个随机的 循序渐进的 发散式的过程)
   ///  
   /// 2 单例有什么用,为什么要用单例,什么时候用单例? 
   ///   节省资源? 对象常驻内存,重用对象
   ///              即时申请--使用--释放
   ///   学完单例,就恨不得把全部类型都搞成单例,这是不对的
   ///   同一个实例,不能解决多线程并发问题!!!
   ///   
   ///   单例就是程序只需要这个对象实例化一次,只允许实例化
   ///   数据库连接池:数据库连接--非托管资源--申请/释放消耗性能
   ///                 池化资源--内置10个链接---使用来拿,用完放回来--避免重复申请和销毁
   ///                 ---控制链接数量
   ///   线程池  流水号生成器   配置文件读取   IOC容器实例
   ///  
   /// 3 继续提问,流水号生成器怎么实现的 为啥单例 性能如何。。。
   /// 
   /// 
   /// 老师今天做了个新尝试,把面试+教学融为一炉,还夹杂一些面试心得,
   /// 如果同学们觉得挺有收获的 大开眼界  受到启发的,
   /// 觉得Eleven讲的不错,真的很有才,刷个666,给老师点个赞
   /// 
   /// </summary>
   class Program
   {
       static void Main(string[] args)
       {
           try
           {
            
               ////5个不同的Singleton实例
               //for (int i = 0; i < 5; i++)
               //{
               //    Singleton singleton = new Singleton();
               //    singleton.Show();
               //}

               //for (int i = 0; i < 5; i++)
               //{
               //    Singleton singleton = Singleton.CreateInstance();
               //    singleton.Show();
               //}

               //for (int i = 0; i < 5; i++)
               //{
               //    Task.Run(() =>//启动一个线程执行这些动作;可以认为5个同时执行
               //    {
               //        Singleton singleton = Singleton.CreateInstance();
               //        singleton.Show();
               //    });
               //}
               //{
               //    for (int i = 0; i < 5; i++)
               //    {
               //        SingletonSecond singletonSecond = SingletonSecond.CreateInstance();
               //    }
               //}
               //for (int i = 0; i < 5; i++)
               //{
               //    Task.Run(() =>
               //    {
               //        SingletonThird singletonThird = SingletonThird.CreateInstance();
               //    });
               //}

               List<Task> taskList = new List<Task>();
               for (int i = 0; i < 10000; i++)
               {
                   taskList.Add(Task.Run(() =>
                   {
                       Singleton singleton = Singleton.CreateInstance();
                       singleton.Invoke();
                   }));
               }
               Task.WaitAll(taskList.ToArray());//等待1w个Task都完成
               {
                   Singleton singleton = Singleton.CreateInstance();
                   Console.WriteLine(singleton.Num);
                   //Num:0   1   10000  还是其他
               }
               //0 大错特错! 全局都是一个实例
               //10000 单例知道   线程安全问题

           }
           catch (Exception ex)
           {
               Console.WriteLine(ex.Message);
           }
           Console.Read();
       }
   }

   //影   单例对象能克隆吗
   //怎么保证是10000呢
}

Singleton.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SingletonPattern
{
   /// <summary>
   /// 单例类:一个构造对象很耗时耗资源类型
   /// 
   /// 懒汉式
   /// </summary>
   public sealed class Singleton
   {
       /// <summary>
       /// 对象会持有资源
       /// </summary>
       private List<string> ConnList = new List<string>()
       {
           "这里是数据库连接1",
           "这里是数据库连接2",
           "这里是数据库连接3",
           "这里是数据库连接4",
           "这里是数据库连接5"//一些其他资源
       };

       /// <summary>
       /// 构造函数耗时耗资源
       /// </summary>
       private Singleton()
       {
           long lResult = 0;
           for (int i = 0; i < 10000000; i++)
           {
               lResult += i;
           }
           Thread.Sleep(1000);
           Console.WriteLine($"{this.GetType().Name}被构造一次");
       }

       private static Singleton _Singleton = null;
       private static readonly object Singleton_Lock = new object();
       public static Singleton CreateInstance()
       {
           //开始多线程初始化---lock锁定--线程请求锁--开始判断创建
           //以上多线程都结束后---再来多个线程请求呢?--都需要等待锁
           //--拿到锁--进去判断--不为空--返回对象--。。。。
           //有点浪费,不需要等待锁,直接判断就行了
           if (_Singleton == null)
           {
               lock (Singleton_Lock)//可以保证任意时刻只有一个线程进入,其他线程等待
               {
                   if (_Singleton == null)//这个判断不能去掉,保证只初始化一次
                   {
                       _Singleton = new Singleton();
                   }
               }
           }
           return _Singleton;
       }

       public static void DoNothing()
       { }

       public void Show()
       {
           Console.WriteLine($"这里是{this.GetType().Name}.Show");
       }

       public int Num = 0;//不是线程安全的  10000个线程同时来+1  结果一定小于10000
       /// <summary>
       /// 一个操作,有个计数
       /// </summary>
       public void Invoke()
       {
           this.Num++;
       }

   }
}

SingletonSecond.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SingletonPattern
{
   /// <summary>
   /// 饿汉式
   /// </summary>
   public sealed class SingletonSecond
   {
       /// <summary>
       /// 对象会持有资源
       /// </summary>
       private List<string> ConnList = new List<string>()
       {
           "这里是数据库连接1",
           "这里是数据库连接2",
           "这里是数据库连接3",
           "这里是数据库连接4",
           "这里是数据库连接5"//一些其他资源
       };

       /// <summary>
       /// 构造函数耗时耗资源
       /// </summary>
       private SingletonSecond()
       {
           long lResult = 0;
           for (int i = 0; i < 10000000; i++)
           {
               lResult += i;
           }
           Thread.Sleep(1000);
           Console.WriteLine($"{this.GetType().Name}被构造一次");
       }

       private static SingletonSecond _SingletonSecond = null;
       /// <summary>
       /// 静态构造函数:由CLR保证,在第一次使用到这个类型之前,自动被调用且只调用一次
       /// 很多初始化都可以写在这里
       /// </summary>
       static SingletonSecond()
       {
           _SingletonSecond = new SingletonSecond();
       }
       public static SingletonSecond CreateInstance()
       {
           return _SingletonSecond;
       }
       public static void DoNothing()
       { }
       public void Show()
       {
           Console.WriteLine($"这里是{this.GetType().Name}.Show");
       }

       public void Log(string text)
       {

       }

   }
}

SingletonThird.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SingletonPattern
{
   /// <summary>
   /// 饿汉式
   /// </summary>
   public sealed class SingletonThird
   {
       /// <summary>
       /// 对象会持有资源
       /// </summary>
       private List<string> ConnList = new List<string>()
       {
           "这里是数据库连接1",
           "这里是数据库连接2",
           "这里是数据库连接3",
           "这里是数据库连接4",
           "这里是数据库连接5"//一些其他资源
       };

       /// <summary>
       /// 构造函数耗时耗资源
       /// </summary>
       private SingletonThird()
       {
           long lResult = 0;
           for (int i = 0; i < 10000000; i++)
           {
               lResult += i;
           }
           Thread.Sleep(1000);
           Console.WriteLine($"{this.GetType().Name}被构造一次");
       }
       /// <summary>
       /// 静态字段:由CLR保障,在第一次使用这个类型之前,会自动初始化且只初始化一次
       /// </summary>
       private static SingletonThird _SingletonThird = new SingletonThird();
       public static SingletonThird CreateInstance()
       {
           return _SingletonThird;
       }

       public void Show()
       {
           Console.WriteLine($"这里是{this.GetType().Name}.Show");
       }

       public void Log(string text)
       {

       }

   }
}

最后,想了解更多,可加入CCNetCore社区,橙子带你走上.netcore学习之路。
你可以免费获取到:

  • 设计模式
  • 面试八股文
  • 问题答疑
  • 闲聊茶馆
  • Asp.Netcore图文源码解读
  • 第一代软件架构单体应用相关技术
  • 第二代软件架构分布式应用相关技术
  • 第三代软件架构微服务应用相关技术
  • 第四代软件架构网格服务应用相关技术

站点网址:ccnetcore.com

上一篇:03_单例设计模式


下一篇:高效的vscode按键绑定