Singleton(单例模式)
1. 意图
在开发中,若某些模块或功能只需要一个类实例,所有调用地方通过着一个类对象访问功能,单例模式符合这种类实例创建模式,并且通过提供统一类实例接口访问类对象。
2. 适用性
《Gof 设计模式-可复用面向对象软件的基础》中对此模式的适用性描述如下:
- 当类只能有一个实例且客户可以从一个公众的访问点访问。
- 当这个唯一实例应该是通过子类化可拓展的,并且客户应该无需更改代码就能使用一个扩展的实例时。
3. 实现
- 饿汉模式:类加载时,类对象创建并初始化,调用时直接使用已经创建好的。
#include <iostream>class Singleton {
public:static Singleton *GetInstance() { return m_instance; }void Print() { std::cout << __FUNCTION__ << std::endl; }private:Singleton() = default;~Singleton() = default;Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;private:static Singleton *m_instance;
};Singleton *Singleton::m_instance = new Singleton;void Test() { Singleton::GetInstance()->Print(); }int main() {Test();return 0;
}
- 懒汉模式:类对象创建与初始化被延迟到真正调用的位置。
#include <iostream>class Singleton {
public:static Singleton *GetInstance() {if (m_instance == nullptr)m_instance = new Singleton;return m_instance;}void Print() { std::cout << __FUNCTION__ << std::endl; }private:Singleton() = default;~Singleton() = default;Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;private:static Singleton *m_instance;
};Singleton *Singleton::m_instance = nullptr;void Test() { Singleton::GetInstance()->Print(); }int main() {Test();return 0;
}
懒汉模式存在多线程并发问题,可以加锁,如下
#include <iostream>
#include <mutex>std::mutex mtx;class Singleton {
public:static Singleton *GetInstance() {std::lock_guard<std::mutex> locker(mtx);if (m_instance == nullptr)m_instance = new Singleton;return m_instance;}void Print() { std::cout << __FUNCTION__ << std::endl; }private:Singleton() = default;~Singleton() = default;Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;private:static Singleton *m_instance;
};
以上通过加锁保证了数据的并发安全,但若此对象创建好后多个线程频繁调用,每次都加锁访问可读对象,对程序性能影响较大,于是又出现了双层检查机制,优化访问性能。
#include <iostream>
#include <mutex>std::mutex mtx;class Singleton {
public:static Singleton *GetInstance() {if (m_instance == nullptr) {std::lock_guard<std::mutex> locker(mtx);if (m_instance == nullptr)m_instance = new Singleton;}return m_instance;}void Print() { std::cout << __FUNCTION__ << std::endl; }private:Singleton() = default;~Singleton() = default;Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;private:static Singleton *m_instance;
};Singleton *Singleton::m_instance = nullptr;void Test() { Singleton::GetInstance()->Print(); }int main() {Test();return 0;
}
双重检查机制实际上还存在潜在的问题,内存访问重新排序(重新排列编译器产生的汇编指令)导致双重锁定失效(考虑类对象内存分配和调用构造函数初始化分为两步执行,指令不顺序执行就无法保证多线程有其它指令在这两步之间执行)。所以需要保证指令顺序执行,避免指令重排。
#include <atomic>
#include <iostream>
#include <mutex>class Singleton {
public:static Singleton *GetInstance() {Singleton *tmp = m_instance.load(std::memory_order_relaxed);std::atomic_thread_fence(std::memory_order_acquire);if (tmp == nullptr) {std::lock_guard<std::mutex> locker(m_mtx);tmp = m_instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton;std::atomic_thread_fence(std::memory_order_release);m_instance.store(tmp, std::memory_order_relaxed);}}return m_instance;}void Print() { std::cout << __FUNCTION__ << std::endl; }private:Singleton() = default;~Singleton() = default;Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;private:static std::atomic<Singleton *> m_instance;static std::mutex m_mtx;
};std::atomic<Singleton *> Singleton::m_instance = nullptr;
std::mutex Singleton::m_mtx;void Test() { Singleton::GetInstance()->Print(); }int main() {Test();return 0;
}
以上使用原子变量及内存序约束实现单例类,避免指令重排问题,同时解决并发问题。但实现略微繁琐,c++11以后对静态变量创建的并发安全提供了保证,简化写法如下:
#include <iostream>class Singleton {
public:static Singleton &GetInstance() {static Singleton instance;return instance;}void Print() { std::cout << __FUNCTION__ << std::endl; }private:Singleton() = default;~Singleton() = default;Singleton(const Singleton &) = delete;Singleton &operator=(const Singleton &) = delete;
};void Test() { Singleton::GetInstance().Print(); }int main() {Test();return 0;
}
4. 优缺点
- 控制类实例数量
- 比类操作更灵活,减少命名空间污染
- 隐藏了类之间的依赖关系
- 影响代码的扩展性
- 影响代码的可测试性
- 不支持包含参数的构造函数
5. 模板实现
- 单实例管理
template <typename T> class SingletonManager {
public:template <typename... Args> static T &GetInstance(Args &&...args) {static T instance(std::forward<Args>(args)...);return instance;}private:SingletonManager() = default;virtual ~SingletonManager() = default;SingletonManager(const SingletonManager &) = delete;SingletonManager &operator=(const SingletonManager &) = delete;
};
- 多实例管理
#include <map>
#include <memory>
#include <string>template <typename T, typename K = std::string> class MultitonManager {
public:template <typename... Args>static T &GetInstance(const K &key, Args &&...args) {return AssignInstance(key, std::forward<Args>(args)...);}template <typename... Args> static T &GetInstance(K &&key, Args &&...args) {return AssignInstance(key, std::forward<Args>(args)...);}private:template <typename Key, typename... Args>static T &AssignInstance(Key &&key, Args &&...args) {auto iter = m_map.find(key);if (iter == m_map.end()) {static T instance;m_map.emplace(key, &instance);return instance;}return *(iter->second);}private:MultitonManager() = default;virtual ~MultitonManager() = default;MultitonManager(const MultitonManager &) = delete;MultitonManager &operator=(const MultitonManager &) = delete;private:static std::map<K, T *> m_map;
};template <typename T, typename K> std::map<K, T *> MultitonManager<T, K>::m_map;
相关文章:
Singleton(单例模式)
1. 意图 在开发中,若某些模块或功能只需要一个类实例,所有调用地方通过着一个类对象访问功能,单例模式符合这种类实例创建模式,并且通过提供统一类实例接口访问类对象。 2. 适用性 《Gof 设计模式-可复用面向对象软件的基础》中对…...
【Linux报错】“-bash: cd: too many arguments“
问题描述 今天使用 cd 想要调整某个文件目录时,发现以下报错 原因分析: arguments 是参数的意思,该报错提示参数过多,意味着系统识别到了多余参数 本质原因:你的命令中输入了多余的 ”空格“ ,检查一…...
C# WebService返回参数为DataTable报错“XML文档有错误”
该问题由于DataTable列存在自定义类型。 解决该报错需要以下几步: 1、自定义类型增加xml序列化 2、由于C#从 XML 反序列化 DataSet 或 DataTable 时的默认限制,所以需要先把调用方的项目开放限制,如果是.netframework项目,需要…...
[paddle]paddleseg快速开始
快速开始 为了让大家快速了解PaddleSeg,本文档使用一个简单示例进行演示。在实际业务中,建议大家根据实际情况进行调整适配。 在开始下面示例之前,请大家确保已经安装好PaddleSeg开发环境(安装说明)。 1 准备数据 …...
UNIAPP popper气泡弹层【unibest框架下】vue3+typescript
看了下市场的代码,要么写的不怎么好,要么过于复杂。于是把市场的代码下下来了自己改。200行代码撸了个弹出层组件。兼容H5和APP。 功能: 1)只支持上下左右4个方向的弹层不支持侧边靠齐 2)不对屏幕边界适配 3)支持弹层外边点击自动隐藏 4)支持…...
launcher.py: error: the following arguments are required: --output_dir
记录一个LLaMA-Factroy配置过程。 安装 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e ".[torch,metrics]"训练 CUDA_VISIBLE_DEVICES0 llamafactory-cli train example/train_lora/.yaml按理说配置好文件应…...
C语言基础之结构体
今天我们来讲讲C语言基础的最后一个知识点了 —— 结构体。不知道大家对前面的C语言基础的知识点掌握的怎么样了呢?下面我们就开始讲解结构体的相关知识点吧! 什么是结构体呢?或者说结构体有什么作用呢?对于复杂对象来说ÿ…...
Redis入门第四步:Redis发布与订阅
欢迎继续跟随《Redis新手指南:从入门到精通》专栏的步伐!在本文中,我们将深入探讨Redis的发布与订阅(Pub/Sub)模式。这是一种强大的消息传递机制,适用于各种实时通信场景,如聊天应用、实时通知和…...
MySQL 之权限与授权
MySQL 权限及授权系统用于控制数据库用户对数据库资源的访问和操作权限。它提供了一种细粒度的安全控制机制,确保只有被授权的用户才能执行特定的操作。MySQL 的权限控制体系非常灵活,支持多种权限类型及级别(数据库、表、列、存储过程等&…...
解决方案:Pandas里面的loc跟iloc,有什么区别
文章目录 一、现象二、解决方案案例使用loc使用iloc 简单总结 一、现象 在用Pandas库处理数据的时候,久而久之不用loc跟iloc,难免会有些混乱记混 二、解决方案 在Pandas中,loc和iloc是两种常用的数据选择方法,它们的主要区别在…...
C# 和 C++ 混合编程
以下是一个关于 C# 和 C 混合编程 的教程详细目录,涵盖了混合编程中的各个重要方面: 目录 1. 引言 1.1 什么是混合编程? 1.2 为什么选择 C# 和 C 进行混合编程? 1.3 应用场景和优势 2. 基本概念 2.1 C# 和 C 的基础差异 2.…...
Vxe UI vue vxe-table 实现表格单元格选中功能
Vxe UI vue vxe-table 实现表格单元格选中功能 在表格中实现鼠标点击任意单元格,选取的功能,通过 mouse-config 配置就可以开启单选功能,多选单元格选取功能需安装插件支持。 代码 参数说明 mouse-config 鼠标配置项: selected&…...
组合模式详解
1、组合模式基本介绍 1) 组合模式(Composite Pattern),又叫部分整体模式,它创建了对象组的树形结构,将对象组合成树状结构以 表示“整体-部分”的层次关系。 2) 组合模式依据树形结构来组合对象,用来表示部…...
AltiumDesigner脚本开发-DIP封装制作
1.点击工具栏的运行工具(蓝色向右三角图标)可以执行脚本程序; 2.点击菜单栏Run->Run可以执行脚本程序; 3.在脚本编辑器中,按键盘的F9键可以执行脚本程序; 4.通过菜单栏执行脚本程序(需要将程序添加到菜单栏中&am…...
乌班图基础设施安装之Mysql8.0+Redis6.X安装
简介:云服务器基础设施安装之 Mysql8.0Redis6.X 安装 Docker安装 # 按照依赖 yum install -y yum-utils device-mapper-persistent data lvm2 Docker Mirror 从去年开始. hub.docker.com[1] 在国内的访问速度极慢. 当时大家主要还是依赖国内的一些镜像源: 如中科…...
【动态规划-最长递增子序列(LIS)】力扣673.最长递增子序列的个数
给定一个未排序的整数数组 nums , 返回最长递增子序列的个数 。 注意 这个数列必须是 严格 递增的。 示例 1: 输入: [1,3,5,4,7] 输出: 2 解释: 有两个最长递增子序列,分别是 [1, 3, 4, 7] 和[1, 3, 5, 7]。 示例 2: 输入: [2,2,2,2,2] 输出: 5 解释:…...
SQL优化 where谓词条件is null优化
1.创建测试表及谓词条件中包含is null模拟语句 create table t641 as select * from dba_objects; set autot trace select SUBOBJECT_NAME,OBJECT_NAME from t641 where OBJECT_NAMEWRI$_OPTSTAT_SYNOPSIS$ and SUBOBJECT_NAME is null; 2.全表扫描逻辑读1237 3.创建等值谓词条…...
Starrocks 元数据恢复 failed to load journal type 10242
fe 启动异常 2024-10-08 09:24:57.66908:00 INFO (stateChangeExecutor|87) [DatabaseTransactionMgr.replayUpsertTransactionState():1702] remove expired transaction: TransactionState. txn_id: 189324, label: delete_031c5090-7e2d-11ef-bdd8-000c29967e13, db id: 100…...
《深度学习》神经语言模型 Word2vec CBOW项目解析、npy/npz文件解析
目录 一、关于word2vec 1、什么是word2vec 2、常用训练算法 1)CBOW 2)SkipGram 二、关于npy、npz文件 1、npy文件 1)定义 2)特性 3)用途 4)保存及读取 运行结果: 运行结果…...
黄粱一梦,镜花水月总是空
总有人间一两风,埋我十万八千梦 自古以来,梦在我们的生活中一直是一个神秘玄幻而又发人深省的存在,我们一生中有三分之一的时间都在睡觉,做过的梦也是丰富多彩数不胜数。 而从科学的角度来说,梦是我们潜意识里的生活…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...
