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

2023-11-04

目录

一、概述

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("实例化!");
            }
        }
    }
  • 结果

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

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

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

    • 多线程情况下产生问题
    • 结果:多次实例化
    • 解释

      • 打印多个?因为同一个时间片完成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<PrA> list = new List<PrA>();

        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<ss> list_ss = new();
        public int id = 0;
    }

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C#设计模式——单例模式(Singleton Pattern) 的相关文章

  • WPF DataGrid 多选

    我读过几篇关于这个主题的文章 但很多都是来自 VS 或框架的早期版本 我想做的是从 dataGrid 中选择多行并将这些行返回到绑定的可观察集合中 我尝试创建一个属性 类型 并将其添加到可观察集合中 它适用于单个记录 但代码永远不会触发多个
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • 方程“a + bx = c + dy”的积分解

    在等式中a bx c dy 所有变量都是整数 a b c and d是已知的 我如何找到整体解决方案x and y 如果我的想法是正确的 将会有无限多个解 由最小公倍数分隔b and d 但我只需要一个解决方案 我可以计算其余的 这是一个例
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 结构体的内存大小不同?

    为什么第一种情况不是12 测试环境 最新版本的 gcc 和 clang 64 位 Linux struct desc int parts int nr sizeof desc Output 16 struct desc int parts
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • bash_profile和.bashrc的区别

    1 etc profile 此文件为系统的每个用户设置环境信息 当用户第一次登录时 该文件被执行 并从 etc profile d目录的配置文件中搜集shell的设置 2 etc bashrc 为每一个运行bash shell的用户执行此文
  • Java中关于thread的停止问题

    stop Deprecated public final void stop Throwable obj 已过时 该方法具有固有的不安全性 请参阅 stop 以获得详细信息 该方法的附加危险是它可用于生成目标线程未准备处理的异常 包括若没有
  • DES加密Delphi、C#互通(CBC加密模式)

    Delphi 目录 https blog csdn net dkbnull article details 87935698 unit Unit1 interface uses Windows Classes SysUtils Dialog
  • Docker实战-编写Dockerfile

    一 编译镜像 1 编译镜像 Dockerfile类似于Makfile 用户使用docker build就可以编译镜像 使用该命令可以设置编译镜像时使用的CPU数量 内存大小 文件路径等 语法 docker build OPTIONS PAT
  • 初学Spring框架

    Spring 是一个java 项目开发的框架技术 所谓框架可以看成一个项目的半成品 已经具备了一个项目项目的基本骨架部分 需要自己实现一些具体的内容 Spring官网 初学Spring框架 Spring Framework 简介 IoC 入
  • Unity实战篇:讨论动画过程和计算伤害之间的关系

    在开发游戏的时候 我们会遇到这样的需求 当人物的攻击落到敌人身上时 播放特效 声效 产生伤害 等等一系列要求 那么我们要怎么实现呢 先了解一下怎么添加动画帧事件 https blog csdn net qq 15020543 article
  • Matlab学习5-图像处理之图像乘法、除法、边缘检测

    Matlab学习5 图像处理之逻辑运算 图像乘法 除法 边缘检测 1 图像乘法 效果 代码 图像相乘 img1 imread img rice png img2 imread img F4 11b MASK bmp img3 immulti
  • 【Getting Started with LLVM Core Libraries】P30 3.4 实验 使用独立工具

    LLVM学习笔记 Getting Started with LLVM Core Libraries P30 3 4 实验 使用独立工具 我们来看一个由分散在多个源文件中的函数组成的简单的C程序 但是 我们使用独立工具也可以获得相同的结果 为
  • 重定向爬虫和多线程爬虫

    前言 重定向爬虫是指在抓取网页时 如果目标网站内部存在重定向机制 即当你访问一个网页时 服务器会把你重定向到另一个目标网页 重定向爬虫可以帮助我们发现这种重定向链接 从而更有效地抓取目标网站的内容 要实现重定向爬虫 你需要在爬虫代码中添加重
  • 【云原生进阶之PaaS中间件】第一章Redis-1.2数据类型

    1 Redis 数据类型 Redis支持五种数据类型 string 字符串 hash 哈希 list 列表 set 集合 及zset sorted set 有序集合 1 1 String 字符串 string是redis最基本的类型 你可以
  • GridView横向滚动

    GridView和ListView都是android比较重要的控件 但是横滚的控件不是太多 这里介绍怎么把GridView横向滚动起来 看到其他网友也有相应的解决方法 自己只是把这些知识总结一下 供大家参考 首先让GridView横向滚动需
  • MySQL慢查询优化

    目录 优化方式 sql语句优化 索引优化 explain分析执行计划 show profile分析SQL trace分析优化器执行计划 通过慢查询日志定位那些执行效率低的SQL语句 MySQL大分页查询问题 一文详解MySQL各种锁及MVC
  • Android获取assets子目录注意事项

    获取assets子目录方法 span style font size 14px String subList getAssets list subdir span 返回当前目录下的所有文件 夹名 但不包含当前目录下子目录的文件 夹名 如果需
  • react源码中的hooks

    今天 让我们一起深入探究 React Hook 的实现方法 以便更好的理解它 但是 它的各种神奇特性的不足是 一旦出现问题 调试非常困难 这是由于它的背后是由复杂的堆栈追踪 stack trace 支持的 因此 通过深入学习 React 的
  • nacos的使用(借鉴淘宝Diamond配置中心)

    nacos常见的使用方法无非就是将获取到的配置文件放入propertise文件中 再从propertise中获取内容 如果配置过多 每增加一个nacos配置就得加一个propertise文件 文件多了 不便于维护 像一些需要经常改动的配置
  • softmax(二):softmax交叉熵不是真正的目标函数

    最近一直在消化 吸收大神的输出 在这里主要把思路捋顺 试着增加自己的理解和扩展 首先 上链接 还有我的膝盖 拜王峰大神 可以直接看大神的文字 从最优化的角度看待Softmax损失函数 Softmax理解之Smooth程度控制 一 优化的目标
  • 常用的设计模式

    单例模式 简单点说 就是一个应用程序中 某个类的实例对象只有一个 你没有办法去new 因为构造器是被private修饰的 一般通过getInstance 的方法来获取它们的实例 getInstance 的返回值是一个对象的引用 并不是一个新
  • 数据库MySQL-索引(含常见面试题)

    目录 一 什么是索引 二 索引的作用 三 索引优缺点及场景 1 优点 2 缺点 3 使用场景 4 注意事项 四 索引的使用 1 索引分类 2 查看索引 3 创建索引 重点 4 索引和约束的区别 容易混淆 五 索引实现原理 索引失效 2 实现
  • Python——queue

    Queue Queue是python标准库中的线程安全的队列 FIFO 实现 提供了一个适用于多线程编程的先进先出的数据结构 即队列 用来在生产者和消费者线程之间的信息传递 基本FIFO队列 class Queue Queue maxsiz
  • C#设计模式——单例模式(Singleton Pattern)

    目录 一 概述 1 基础 二 实现 1 单线程 2 解决 多线程情况下 解决方案一 Sleep 解决方案二 加锁 三 扩展 一 概述 单例模式 gt 创建型设计模式 定义 保证一个类只有一个实例 并提供一个访问它的全局访问点 在第一个使用者