单例模式由浅入深(C# 版)

2023-10-27

单例模式由浅入深(C# 版)

有时候,我们希望某类只有一个实例,这样的好处是:
1.可以实现数据共享
2.避免大量的创建销毁实例的操作,提高性能

为了实现单例模式,通常做法是:
1.将构造函数私有化,避免外部直接new对象
2.对外提供一个方法来返回一个单例对象实例

最简单的代码像这样:

class SingleTest
{
    private static SingleTest singleTest;
    public int Cnt { get; private set; }
    private SingleTest() { }
    public static SingleTest GetSingleObject()
    {
        if(singleTest is null)
        {
            singleTest = new SingleTest();
            Console.WriteLine("实例被创建");
        }
        return singleTest;
    }
    public void Increase()
    {
        Cnt++;
    }
}

这样的话,外部可以通过静态方法获取单例实例,多次获取到的实例其实是同一个实例

static void Main(string[] args)
{
	SingleTest singleTest = SingleTest.GetSingleObject();
	SingleTest singleTest2 = SingleTest.GetSingleObject();
	Console.WriteLine($"singleTest == singleTest2 ? {singleTest == singleTest2}"); //True
	
	singleTest2.Increase();
	Console.WriteLine(singleTest.Cnt); // 1
}

以上的单例模式的实现被称为懒汉式,所谓懒,就是指只有调用了GetSingleObject()方法,才会去创建实例,不调用GetSingleObject()方法是不可能创建出实例的

以上的代码存在线程不安全的问题,如果多线程同时调用GetSingleObject()方法,会产生多个实例

class Program
{
    private static object lockObject = new();
    static void Main(string[] args)
    {
        List<Task> tasks = new();

        for (int i = 0; i < 100; i++)
        {
            tasks.Add(Task.Run(() =>
            {
                SingleTest singleTest = SingleTest.GetSingleObject();
                lock (lockObject)
                {
                    singleTest.Increase();
                }
            }));
        }
        Task.WaitAll(tasks.ToArray());

        SingleTest singleTest = SingleTest.GetSingleObject();
        Console.WriteLine(singleTest.Cnt);
    }
}

运行以上代码发现"实例被创建"打印了多次

为了解决以上问题,可以使用双重校验加锁的方式,增加一个属性用于加锁并且修改GetSingleObject方法

private static object lockObject = new();
public static SingleTest GetSingleObject()
{
    if(singleTest is null)
    {
        lock (lockObject)
        {
            if (singleTest is null)
            {
                singleTest = new SingleTest();
                Console.WriteLine("实例被创建");
            }
        }
    }
    return singleTest;
}

其中lock加锁保证同时只能有一个线程进入,这样就不会创建多个单例
lock外部的if判断是为了提高效率,因为如果去掉这个if,所有线程都需要等lockObject锁,但是这样的等待不一定的必须的,因为当singleTest不为null时完全不需要等锁,因此加上这个if语句来提高效率
lock内部的if判断显然是必须的,因此并不是线程一旦获得了锁就要创建实例,如果去掉,还是会造成创建多个实例的问题

单例模式(懒汉式线程安全)完整代码是这样的:

class SingleTest
{
    private static SingleTest singleTest;
    private static object lockObject = new();
    public int Cnt { get; private set; }
    private SingleTest() { }
    public static SingleTest GetSingleObject()
    {
        if(singleTest is null)
        {
            lock (lockObject)
            {
                if (singleTest is null)
                {
                    singleTest = new SingleTest();
                    Console.WriteLine("实例被创建");
                }
            }
        }
        return singleTest;
    }
    public void Increase()
    {
        Cnt++;
    }
}

还有一种被称为饿汉式的单例模式,原理是利用静态构造方法只调用一次静态属性初始化时赋值一次的性质来保证实例只有一个

1.利用静态构造方法只调用一次的性质实现饿汉式的单例模式

class SingleTest
{
    private static SingleTest singleTest;
    public int Cnt { get; private set; }
    static SingleTest()
    {
        singleTest = new SingleTest();
        Console.WriteLine("实例被创建");
    }
    public static SingleTest GetSingleObject()
    {
        return singleTest;
    }
    public void Increase()
    {
        Cnt++;
    }
}

2.利用静态属性初始化时赋值一次的性质实现饿汉式的单例模式

class SingleTest
{
    private static SingleTest singleTest = new();
    public int Cnt { get; private set; }
    private SingleTest()
    {
    }
    public static SingleTest GetSingleObject()
    {
        return singleTest;
    }
    public void Increase()
    {
        Cnt++;
    }
}

所谓“饿汉式”的单例模式,意思是即使不调用GetSingleObject方法,SingleTest初始化时就会把实例创建好,展现出一种迫不及待的感觉,因此称为“饿汉式”
显然,饿汉式的单例模式是线程安全的

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

单例模式由浅入深(C# 版) 的相关文章

  • 如何在 Visual Studio 2010 中增强 XAML 设计器?

    当我使用 XAML 设计器时 进入设计器和退出设计器是如此困难和缓慢 当我这样做时 Visual Studio 卡了一段时间 有什么方法可以增强 XAML 设计器和编辑器吗 Ant 保存 XAML 文件时非常慢 这通常意味着您可能有复杂的
  • 使用 Unity 在构造函数中使用属性依赖注入

    好的 我在基类中定义了一个依赖属性 我尝试在其派生类的构造函数内部使用它 但这不起作用 该属性显示为 null Unity 在使用 container Resolve 解析实例后解析依赖属性 我的另一种选择是将 IUnityContaine
  • 如何检查QProcess是否正确执行?

    QProcess process sdcompare QString command sdcompare QStringList args sdcompare command sdcompare diff args sdcompare lt
  • 为 Visual Studio 2013 编译 Tesseract

    我正在尝试使用tesseract在 Visual Studio 2013 中 我在链接器 gt 输入 不是 libtesseract302 static lib 中使用 libtesseract302 lib 一切都正常 并且已编译并运行
  • 如何在 C# 中从 UNIX 纪元时间转换并考虑夏令时?

    我有一个从 unix 纪元时间转换为 NET DateTime 值的函数 public static DateTime FromUnixEpochTime double unixTime DateTime d new DateTime 19
  • 如何将 #ifdef DEBUG 添加到 Xcode?

    我的项目中有一些代码永远不应该在发布版本中使用 但在测试时很有用 我想做这样的事情 ifdef DEBUG Run my debugging only code endif 在 Xcode 4 中哪里添加 DEBUG 设置 我尝试将其放入
  • 互斥体实现可以互换(独立于线程实现)

    所有互斥体实现最终都会调用相同的基本系统 硬件调用吗 这意味着它们可以互换吗 具体来说 如果我使用 gnu parallel算法 使用openmp 并且我想让他们称之为线程安全的类我可以使用boost mutex用于锁定 或者我必须编写自己
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 如何访问另一个窗体上的ListView控件

    当单击与 ListView 所在表单不同的表单中的按钮时 我试图填充 ListView 我在 Form1 中创建了一个方法以在 Form2 中使用 并将参数传递给 Form1 中的方法 然后填充 ListView 当我调试时 我得到了传递的
  • 用于检查项目文件中的项目变量和引用路径的 api

    我正在研究一个 net application VS2010 与 x 没有 解和变量号这些解决方案中的项目数量 我需要检查项目属性 特定于一定数量的项目 是否同质 并且检查 验证构建期间的参考路径 有没有一个API是这样的吗 如果没有 我该
  • 单击 form2 上的按钮触发 form 1 中的方法

    我对 Windows 窗体很陌生 我想知道是否可以通过单击表单 2 中的按钮来触发表单 1 中的方法 我的表格 1 有一个组合框 我的 Form 2 有一个 保存 按钮 我想要实现的是 当用户单击表单 2 中的 保存 时 我需要检查表单 1
  • 将 Excel 导入到 Datagridview

    我使用此代码打开 Excel 文件并将其保存在 DataGridView 中 string name Items string constr Provider Microsoft Jet OLEDB 4 0 Data Source Dial
  • 私有模板函数

    我有一堂课 C h class C private template
  • HttpWebRequest 在第二次调用时超时

    为什么以下代码在第二次 及后续 运行时超时 代码挂在 using Stream objStream request GetResponse GetResponseStream 然后引发 WebException 表示请求已超时 我已经尝试过
  • 有人可以提供一个使用 Amazon Web Services 的 itemsearch 的 C# 示例吗

    我正在尝试使用 Amazon Web Services 查询艺术家和标题信息并接收回专辑封面 使用 C 我找不到任何与此接近的示例 所有在线示例都已过时 并且不适用于 AWS 的较新版本 有一个开源项目CodePlex http www c
  • C++ 密码屏蔽

    我正在编写一个代码来接收密码输入 下面是我的代码 程序运行良好 但问题是除了数字和字母字符之外的其他键也被读取 例如删除 插入等 我知道如何避免它吗 特q string pw char c while c 13 Loop until Ent
  • 为什么在setsid()之前fork()

    Why fork before setsid 守护进程 基本上 如果我想将一个进程与其控制终端分离并使其成为进程组领导者 我使用setsid 之前没有分叉就这样做是行不通的 Why 首先 setsid 将使您的进程成为进程组的领导者 但它也
  • 如何在 C# 中调整图像大小同时保持高质量?

    我从这里找到了一篇关于图像处理的文章 http www switchonthecode com tutorials csharp tutorial image editing saving cropping and resizing htt
  • Server.MapPath - 给定的物理路径,预期的虚拟路径

    我正在使用这行代码 var files Directory GetFiles Server MapPath E ftproot sales 在文件夹中查找文件 但是我收到错误消息说 给定物理路径但虚拟路径 预期的 我对在 C 中使用 Sys
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the

随机推荐

  • 抱薪者说

    Conflux 网络上线一周年在即 在这将近一年的时间里 Conflux 链上合约部署超 5 266 个 用户数超 476 381 共 24 129 394 笔交易被处理 链上集合了 400 开发者 共书写代码超 291 558 行 网络代
  • 在 Python 中如何实现类的继承,方法重载及重写?

    作者 苏凉 py 来源 CSDN博客 今天我们将进入类的继承以及对类的方法重写及重载的学习 话不多说直接进入正题 类的继承 如果要编写的类是另一个现成类的特殊版本 那我们就可以使用继承 一个类继承另一个类时 将自动获得另一个类的所有属性和方
  • 使用 FFmpeg 生成 ts 切片并使用 AES-128 加密

    前言 最近有个需求 需要将服务器视频资源进行加密提供给客户端播放 防止用户盗用视频 常用的加密方式 m3u8切片加密 本文使用 各种在线播放视频的网站广泛使用的技术 切片同样是使用AES加密算法 优点 各种浏览器 手机 小程序都能兼容 通用
  • CommonJS、AMD、CMD、ES Module

    依赖前置和依赖就近 RequireJS采用依赖前置 举个例子就是炒菜前一次性把所有食材洗好 切好 根据内部逻辑把有些酱料拌好 最后开始炒菜 前面任何一个步骤出现问题都能较早发现错误 SeaJS的依赖就近就是要炒青椒了去切青椒要炒肉了去切肉
  • docker容器资源控制cgroup

    命名空间 六种 namespace 资源配额 cgroups mount t cgroup cd sys fs cgroup cd memory 默认是没有限制 现在更改内存使用 free m mount t cgroup bc 1024
  • 金额转化中文算法

    最近要项目中用到了把数字类型的金额 1029 89元 转换成中文书写的方式 一仟零贰拾玖点八九元 参考了一些其他人写的算法 总觉得有些不太完善或者不严谨 例如10100转换成 十万一千元 还是 十万零一千元 我看到的一些算法都是转换成了前者
  • 解放生产力,社媒运营人还能这样玩转ChatGPT?

    相信大家这段时间都被ChatGPT刷屏了吧 东哥我也不例外 基本上一打开社媒平台都是在讨论ChatGPT 那社媒运营人应该如何使用ChatGPT呢 东哥今天就跟大家唠唠 利用ChatGPT写广告标语 广告文案 运营人常常为广告标语 广告文案
  • 一碗云南米线,加剧速食食品赛道“内卷”?

    说起云南 人们的印象往往是藏在苍山洱海 玉龙雪山里的风花雪月 然而 生活中最常见的 滇味 却是一碗鲜香美味 软中带劲的米线 近年来 从广西的螺蛳粉到河南的酸辣粉 越来越多带着地域特色的主食被装进小小纸桶 成为速食食品行业的新品类 如今 云南
  • 非科班进大厂必备算法

    基础数据结构的融合是成为庞大系统的基石 比如 Redis 中的跳跃表 数据库索引 B 树等 只有对基础的数据结构足够的熟悉才能更容易去理解稍微复杂的结构 就仿佛我们闯关打怪一样 一步一步解锁直到结局 今天想和大家一起分享的是常见数据结构以及
  • @Lazy标签解决问题的原理

    Lazy原理解析 文章目录 Lazy原理解析 1铺垫知识点 2 lazy解决问题原理 3 lazy为什么不能用在原型模式中 4其他解决循环依赖的办法 最近在重构一个国重项目的时候 用 lazy解决了循环依赖的问题 在自己的类中又依赖了自己
  • 图像恢复系列(11)之修复(inpainting)

    十一 图像恢复 修复 27 WaveFill A Wavelet based Generation Network for Image Inpainting 图像修复旨在用逼真的内容完成图像缺失或损坏的区域 当前流行的方法通过使用生成对抗网
  • 金山文档手机app服务器异常,为什么我的手机看不了金山文档 看不了金山文档怎么办...

    1 手机wps上找到 我的office 点击上面的 登录wps 使用有关的账号登录 2 使用的QQ账号登录 登录成功 就可以使用wps的云文档功能了 可以将文档加入文档 只要在电脑上登录就可以同步查看了 3 在菜单当中找到 打开 在 导入到
  • 推荐三款最好用的压缩/解压软件

    写在前面 推荐三款特别好用的压缩 解压软件 Bandizip WinRAR 7 Zip 这三款软件也分别代表了三种常用的压缩格式 zip rar 7z 压缩格式 格式 优点 速度 zip 兼容性好 快 rar 私有格式 中 7z 压缩率高
  • 数据库架构在美团点评的演变实践

    前言 IT时代的缩影基本被CPU 操作系统 数据库这三大核心领域 支撑了半个世纪人类的商业科技文明 本文讲到美团点评数据库的演变 首先从数据库的简史讲起 把数据库划分成三个时代 分别就是关系型数据库时代 NoSQL时代 NewSQL时代 下
  • 【Kaggle】Stable Diffusion 竞赛(2023 年 5 月 11 日版本,准确率 0.59 + )

    一 第一部分讲解 mkdir p kaggle images from PIL import Image from pathlib import Path images list Path kaggle input stable diffu
  • android 轮播图_两步路户外助手谷歌卫星图终极解决方案

    不点蓝字 我们哪来故事 国庆节前 教大家一步步搞回了两步路 户外助手 的 谷歌卫星图 和 路网 错过的朋友可以看之前的文章 快速找回 两步路 户外助手 的谷歌卫星图 路网 但是文章发出后陆续收到一两个驴友反馈 路网是回来了 但是还是没有谷歌
  • linux ssh权限设置,linux 让ssh只允许指定的用户登录的权限设置

    方法一 只允许ssh指定用户登录权限的设置 SSH远程登录的权限直接影响服务器的安全 为ssh设置合理的用户权限是必须的 查看ssh版本的命令 ssh v 设置ssh只允许指定用户登录的方法 在 etc ssh sshd config文件中
  • 【mysql5.7开启 binlog】

    今天发现数据库连接不上了 一看服务器 家被人偷了 库还在只剩下一个表README 以下数据库已被删除 xxx xxx 我们有完整的备份 要恢复它 您必须向我们的比特币地址xxxx支付0 028比特币 BTC 如果您需要证明 请通过以下电子邮
  • grafana设置Alert阈值和邮件报警

    首先我们需要在机器上开启smtp 25服务 这里有一个坑 注意 云主机为了防止滥发邮件已经封掉了smtp的所有通信 所以云主机发邮件是有问题的 安装sendmail ubuntu用apt centos用yum 安装好后一般自动就跑起来了 n
  • 单例模式由浅入深(C# 版)

    单例模式由浅入深 C 版 有时候 我们希望某类只有一个实例 这样的好处是 1 可以实现数据共享 2 避免大量的创建销毁实例的操作 提高性能 为了实现单例模式 通常做法是 1 将构造函数私有化 避免外部直接new对象 2 对外提供一个方法来返