C++:谈谈单例模式的多种实现形式
文章目录
- 实现 1:静态成员
- 实现 2:atexit + 懒汉模式
- 实现 3:原子变量 + 懒汉模式
- 实现4:atexit + 饿汉模式
- * 实现5:magic static
单例模式:保证一个类仅有一个实例,并提供一个该实例的全局访问点。
- 稳定点:类只有一个实例,提供全局
- 变化点:有多个类都是单例,能否复用代码
实现 1:静态成员
- 构造函数和析构函数私有化
- 禁掉拷贝构造、拷贝赋值、移动构造、移动赋值
- 静态成员函数
- 静态私有成员
前两点针对唯一实例,后两点针对全局访问。
问题:当程序结束后,不会调用析构函数,堆上资源无法释放,内存泄漏。虽然程序结束后,堆上所有的数据被销毁,但是无法保存需要持久化的数据。
class Singleton {
public:// 静态成员函数:全局访问点static Singleton* getInstance() {if(nullptr == _pInstance) {_pInstance = new Singleton();}return _pInstance;}private:// 构造函数和析构函数私有化Singleton(); ~Singleton(); // 禁掉拷贝构造、拷贝赋值、移动构造、移动赋值Singleton(const Singleton &) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton &&) = delete;Singleton& operator=(Singleton &&) = delete;private:// 静态成员:静态成员函数只能访问静态成员static Singleton* _pInstance;};// 静态成员需要初始化
Singleton* Singleton::_instance = nullptr;
实现 2:atexit + 懒汉模式
atexit 函数:在进程结束后利用回调函数自动释放堆空间。
/*
功能:注册给定函数,并在进程结束后调用该函数
参数:函数指针,指向被调用的函数(返回值、参数均为void)
*/
#include <stdlib.h>
int atexit(void (*function)(void));
利用这一特性,在进程结束时 atexit 函数调用销毁函数,完成析构工作。
问题:atexit 函数本身安全,但是多线程环境下。存在线程安全问题。
static Singleton* getInstance() {if(nullptr == _pInstance) {// 问题:多个并发线程可能同时创建对象_pInstance = new Singleton();atexit(Singleton::Destructor);}return _pInstance;
}
为保证线程安全,需要加锁。对于加锁操作,只有第一次写操作创建对象的时候,需要加锁;其他时候都是读操作,没有必要加锁。因此这里在实现的时候可以采用双重检测 double check的技巧。
#include <stdlib.h>
class Singleton {
public:static Singleton* getInstance() {if(nullptr == _pInstance) {_pInstance = new Singleton();// 线程安全,双重检测:double check if (nullptr == _pInstance) {std::lock_guard<std::mutex> lock(_mutex);if (nullptr == _pInstance) {// 问题:多线程环境下,cpu reorder_pInstance = new Singleton();atexit(Singleton::Destructor);}}return _pInstance;// 注册回调函数,进程结束后,调用销毁函数atexit(Singleton::Destructor);}return _pInstance;}private:Singleton(); ~Singleton(); Singleton(const Singleton &) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton &&) = delete;Singleton& operator=(Singleton &&) = delete;// 注册销毁函数为atexit的回调函数,用于在进程结束后释放堆空间static void Destructor() {if (nullptr != _instance) { delete _instance;_instance = nullptr;}}private:static Singleton* _pInstance;};Singleton* Singleton::_instance = nullptr;
问题:new 操作符指令重排
C++ 98 表达单线程语义。而在多核多线程的情况下,若 cpu 指令重排,例如:对于 new 运算符的指令执行:分配内存、调用构造函数、返回指针。若发生 cpu 指令重排,会优化为分配内存、返回指针,却还没有调用构造函数初始化数据。此时,若有其他线程访问,可能造成程序的崩溃。
实现 3:原子变量 + 懒汉模式
C++ 11:多线程语义,cpu 指令重排,提供同步原语:原子变量、内存屏障等
原子变量解决
- 原子性问题
- 可见性问题:load 可以看见其他线程最新操作的数据, store 修改数据让其他线程可见
- 执行序问题:
memory_order_acuire不能重排指令,memory_order_release松散指令,可以重排指令。
内存屏障(内存栅栏)解决
- 可见性问题
- 执行序问题
使用原子变量解决原子性、可见性、执行序
class Singleton {
public:static Singleton * GetInstance() {Singleton* tmp = _instance.load(std::memory_order_acquire);if (tmp == nullptr) {std::lock_guard<std::mutex> lock(_mutex);tmp = _instance.load(std::memory_order_acquire);if (tmp == nullptr) {tmp = new Singleton;_instance.store(tmp, memory_order_release);atexit(Destructor);}}return tmp;}
...static std::atomic<Singleton*> _instance;static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance; // 静态成员需要初始化
std::mutex Singleton::_mutex; // 互斥锁初始化
改进:若构造函数中存在其他原子性操作,则可以使用松散的指令执行方式,提升运行速度。使用内存屏障,避免 tmp 指针在 new 操作未执行完就返回给用户。
- 原子变量解决:原子性、可见性
- 内存栅栏解决:执行序
class Singleton {
public:static Singleton * GetInstance() {Singleton* tmp = _instance.load(std::memory_order_relaxed);// 获取内存屏障std::atomic_thread_fence(std::memory_order_acquire);if (tmp == nullptr) {std::lock_guard<std::mutex> lock(_mutex);tmp = _instance.load(std::memory_order_relaxed);if (tmp == nullptr) {tmp = new Singleton;// 释放内存屏障std::atomic_thread_fence(std::memory_order_release);_instance.store(tmp, std::memory_order_relaxed);atexit(Destructor);}}return tmp;}...static std::atomic<Singleton*> _instance;static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance; // 静态成员需要初始化
std::mutex Singleton::_mutex; // 互斥锁初始化
问题:代码复杂,书写困难。
实现4:atexit + 饿汉模式
懒汉模式是延迟加载,饿汉模式是提前加载。当系统开始运行,加载类的时候就初始化类实例,其他线程无法再创建实例,实现线程安全。
class Singleton {
public:static Singleton* Singleton::getInstance() {if(nullptr == _pInstance) {_pInstance = new Singleton();atexit(Singleton::Destructor);}return _pInstance;}
...
};// 全局初始化,使其在进程创建之前就不为空,防止子进程创建对象
Singleton* Singleton::_instance = getInstance();
问题:无论是否需要该类实例,都必须提前创建。
* 实现5:magic static
源自:C++ effective,C++ 11 magic static 特性,参考官方文档:静态局部变量,推荐使用。
- 如果变量在初始化的时候,并发同时进入声明语句,并发线程会阻塞等待初始化结束。线程安全。
- 静态局部变量首次经过它的声明才会被初始化,在其后所有的调用中,声明都会被跳过。
因此,使用定义在栈上的局部静态变量保存单例对象,具备所有优点:
- 延迟加载
- 系统自动调用析构函数,回收内存
- 没有 new 操作带来的 cpu reorder 操作
- 线程安全
class Singleton {
public:static Singleton& GetInstance() {// magic static// 定义在栈上的局部静态变量,进程结束后自动释放static Singleton instance;return instance;}private:Singleton(); ~Singleton(); Singleton(const Singleton &) = delete;Singleton& operator=(const Singleton&) = delete;Singleton(Singleton &&) = delete;Singleton& operator=(Singleton &&) = delete;
};
相关文章:
C++:谈谈单例模式的多种实现形式
文章目录实现 1:静态成员实现 2:atexit 懒汉模式实现 3:原子变量 懒汉模式实现4:atexit 饿汉模式* 实现5:magic static单例模式:保证一个类仅有一个实例,并提供一个该实例的全局访问点。 稳…...
【Spring Cloud Alibaba】007-Nacos 配置*
【Spring Cloud Alibaba】007-Nacos 配置* 文章目录【Spring Cloud Alibaba】007-Nacos 配置*一、概述1、概述2、对比 spring cloud config二、基本使用1、在管理界面新建配置2、启动权限3、 搭建 nacos-config 服务第一步:引入依赖第二步:修改 yaml 配置…...
《安富莱嵌入式周报》第304期:开源硬件耳机设计,AI单片机STM32N6已确定为M55内核,另外还有新品STM32H5, H50X, H7R, H7S发布
往期周报汇总地址:嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 更新一期视频教程: 第6期ThreadX视频教程:图文并茂吃透RTOS运行机制,任务管理&…...
vuex篇
1.简介(1)vuexVuex 是一个专为 Vue.js 应用程序开发的状态管理模式 库vuex是为vue.js开发的状态管理模式、组件状态集中管理(2)单页面数据流状态发生变化, 视图就重新渲染state发生变化时, 导致view视图发生改变, 视图通过操作action行为, 又会使得state状态发生变化(3)使用场…...
嵌入式开发:在嵌入式应用程序中混合C和C++
许多嵌入式应用程序仍使用c语言编写,但越来越多的嵌入式开发人员现在使用C语言编写程序。某些应用程序甚至共享这两种语言。这有意义吗?C是嵌入式应用中最常用的编程语言。多年来,人们一直期待着向C过渡,但过渡速度相当缓慢。但是࿰…...
【2023/图对比/增强】MA-GCL: Model Augmentation Tricks for Graph Contrastive Learning
如果觉得我的分享有一定帮助,欢迎关注我的微信公众号 “码农的科研笔记”,了解更多我的算法和代码学习总结记录。或者点击链接扫码关注【2023/图对比/增强】MA-GCL: Model Augmentation Tricks for Graph Contrastive Learning 【2023/图对比/增强】MA-…...
TensorBoard自定义修改单条及多条曲线颜色
在深度学习可视化训练过程中,曲线颜色是随机的,想要将好看的曲线颜色图放到论文中,就得自定义曲线颜色,具体方法见下文。 目录一、下载svg文件二、修改svg文件三、修改后曲线颜色对比四、总结一、下载svg文件 在TensorBoard界面中…...
时间和空间复杂度
文章目录 前言 一、算法效率 1.如何评判算法效率? 2.算法的复杂度 二、时间复杂度 1.时间复杂度的定义 2. 大O的渐进表示法 三、空间复杂度 总结 前言 本文章讲解时间与空间复杂度 提示:以下是本篇文章正文内容,下面案例可供参考 一、算法…...
关于Linux下调试
关于Linux下调试 无论是内核(操作系统)还是应用程序,都存在需要调试的情况。 所谓工欲善其事,必先利其器。一个好的称手的工具,对于快速分析问题、定位问题,提高效率,非常有帮助。 除了工具&a…...
理解TP、FP、TN、FN
概念定义 按照常用的术语,将两个类分别称为正类 (positive) 和 负类 (negative)。使用数学表示: 1表示正类 , -1 表示负类。 正类通常是少数类,即样本较少的类(例如有缺陷的零件) 负类通常是多数类&#x…...
软考中级有用吗
当然有用了! 软考“简历”:计算机软件资格考试在全国范围内已经实施了二十多年,近十年来,考试规模持续增长,截止目前,累计报考人数约有五百万人。该考试由于其权威性和严肃性,得到了社会各界及用人单位的广泛认同&…...
计算机网络之IP协议(详解
网络层主管地址管理与路由选择。而IP协议就是网络层中一个非常重要的协议。它的作用就是在复杂的网络环境中确定一个合适的路径。IP协议头格式4位版本号(version) 指定IP协议的版本,目前只有两个版本:IP v4和IP v6.对于IP v4来说,这个值就是4…...
Kubernetes之探针probe
deployment只保证pod的状态为running。如果pod状态是running,但是里面丢失了文件,导致用户不能访问数据,则deployment是不管用的,此时就需要probe来检测pod是否正常工作。 probe是定义在容器里的,可以理解为容器里加的…...
高性能低功耗4口高速USB2.0 HUB NS1.1S 兼容FE1.1
NS1.1S是一款高性能、低功耗4口高速 USB2.0 HUB 控制器,上行端口兼容高速 480MHz和全速12MHz两种模式,4个下行端口兼容高速480MHz、全速12MHz、低速1.5MHz三种模式。 NS1.1S采用状态机单事务处理架构,而非单片机架构,多个事务缓冲…...
通过VS Code轻松连接树莓派
如果您正在使用树莓派作为开发平台,那么通过远程连接VS Code到树莓派是非常方便的一种方法。这样,您可以在Windows或macOS等计算机上开发和测试代码,而不必在树莓派上进行。 以下是通过VS Code远程连接到树莓派的步骤: 1.安装Re…...
图纸等敏感文件数据外发时 如何确保效率和安全性?
很多企业随着业务的发展,需要频繁的与外部供应商、合作伙伴之间进行数据的交换和使用。尤其是制造型企业,可能每天都要与几十、上百家供应商及合作伙伴进行产品数据交换。目前,大多数企业已经在内部实施了PDM/PLM系统,实现了对组织…...
2023年CDGA考试-第4章-数据架构(含答案)
2023年CDGA考试-第4章-数据架构(含答案) 单选题 1.请从下列选项中选择不属于数据架构师职责的选项 A.确保数据架构和企业战略及业务架构一致 B.提供数据和组件的标准业务词汇 C.设计企业数据模型 D.整合企业数据架构蓝图 答案 C 2.请从下列选项中选择不属于企业数据架构…...
理解随机游走
随机游走 基本思想 从一个或一系列顶点开始遍历一张图。在任意一个顶点,遍历者将以概率1-a游走到这个顶点的邻居顶点,以概率a随机跳跃到图中的任何一个顶点,称a为跳转发生概率,每次游走后得出一个概率分布,该概率分布…...
mqtt协议1- 简介和报文格式
文章目录1.mqtt协议1: 简介和报文格式1.1.MQTT概念1.2.数据2.控制报文格式2.1.MQTT数据包结构2.2.固定头2.2.1.控制报文类型2.2.2.标志FLag2.2.3.剩余长度2.3.可变头2.4.有效载荷Payload消息体安全QoS(Quality of Service levels)ref:1.mqtt协议1: 简介和报文格式 Message Que…...
前端用动画快速实现骨架屏效果
一、动画的语法 1.定义动画 keyframes 自定义动画名称 {// 开始from {transform: scale(1);}// 结束to {transform: scale(1.5);} }// 或者还可以使用百分比定义keyframes 动画名称 {// 开始0% {transform: scale(1);}// 结束100% {transform: scale(1.5);} } 2.调用 anima…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
