【11】c++设计模式——>单例模式
单例模式是什么
在一个项目中,全局范围内,某个类的实例有且仅有一个(只能new一次),通过这个唯一的实例向其他模块提供数据的全局访问,这种模式就叫单例模式。单例模式的典型应用就是任务队列。
为什么要使用单例模式
单例模式充当的就是一个全局变量,为什么不直接使用全局变量呢,因为全局变量破坏类的封装,而且不受保护,访问不受限制。

饿汉模式
饿汉模式是在类加载时进行实例化的。
#include<iostream>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{cout << "我是一个饿汉模式单例" << endl;return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义
};
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;//new一个实例;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
懒汉模式
懒汉模式是在需要使用的时候再进行实例化
#include<iostream>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{if (nullptr == m_taskQ){m_taskQ = new TaskQueue;}return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义
};
TaskQueue* TaskQueue::m_taskQ = nullptr;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
在调用**getInstance()**函数获取单例对象的时候,如果在单线程情况下是没有什么问题的,如果是多个线程,调用这个函数去访问单例对象就有问题了。假设有三个线程同时执行了getInstance()函数,在这个函数内部每个线程都会new出一个实例对象。此时,这个任务队列类的实例对象不是一个而是3个,很显然这与单例模式的定义是相悖的。
线程安全问题
对于饿汉模式来说是没有线程安全问题的,在这种模式下访问单例对象时,这个对象已经被创建出来了,要解决懒汉模式的线程安全问题,最常用的解决方案就是使用互斥锁,可以将创建单例对象的代码使用互斥锁锁住:
#include<iostream>
#include<mutex>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{m_mutex.lock();if (nullptr == m_taskQ){cout << "我加了互斥锁" << endl;m_taskQ = new TaskQueue;}m_mutex.unlock();return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义static mutex m_mutex; //定义为静态的,因为静态函数只能使用静态变量
};
mutex TaskQueue::m_mutex;
TaskQueue* TaskQueue::m_taskQ = nullptr;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
在上面代码的10~13 行这个代码块被互斥锁锁住了,也就意味着不论有多少个线程,同时执行这个代码块的线程只能是一个(相当于是严重限行了,在重负载情况下,可能导致响应缓慢)。我们可以将代码再优化一下:
#include<iostream>
#include<mutex>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{if (nullptr == m_taskQ){m_mutex.lock();if (nullptr == m_taskQ){cout << "我加了互斥锁" << endl;m_taskQ = new TaskQueue;}m_mutex.unlock();}return m_taskQ;}private:TaskQueue() = default; //无参构造static TaskQueue* m_taskQ; //静态成员需要在类外定义static mutex m_mutex;
};
mutex TaskQueue::m_mutex;
TaskQueue* TaskQueue::m_taskQ = nullptr;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
双重检查锁定问题
#include<iostream>
#include<mutex>
#include<atomic>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{TaskQueue* taskQ = m_taskQ.load(); //取出来单例的值if (nullptr == taskQ){m_mutex.lock();taskQ = m_taskQ.load();if (nullptr == taskQ){cout << "我加了原子" << endl;taskQ = new TaskQueue;m_taskQ.store(taskQ); //保存到原子变量中}m_mutex.unlock();}return m_taskQ.load();}private:TaskQueue() = default; //无参构造static atomic<TaskQueue*>m_taskQ; //定义为原子变量static mutex m_mutex;
};
mutex TaskQueue::m_mutex;
atomic<TaskQueue*> TaskQueue::m_taskQ;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
对于m_taskQ = new TaskQueue这行代码来说,我们期望的执行机器指令执行顺序是:
(1)分配用来保存TaskQueue对象的内存;
(2)在分配好的内存中构造一个TaskQueue对象(即初始化内存);
(3)使用m_taskQ指针指向分配的内存。
但是对多线程来说,机器指令可能会被重新排列;即:
(1)分配内存用于保存 TaskQueue 对象。
(2)使用 m_taskQ 指针指向分配的内存。
(3)在分配的内存中构造一个 TaskQueue 对象(初始化内存)。
这样重排序并不影响单线程的执行结果,但是在多线程中就会出问题。如果线程A按照第二种顺序执行机器指令,执行完前两步之后失去CPU时间片被挂起了,此时线程B在第3行处进行指针判断的时候m_taskQ 指针是不为空的,但这个指针指向的内存却没有被初始化,最后线程 B 使用了一个没有被初始化的队列对象就出问题了(出现这种情况是概率问题,需要反复的大量测试问题才可能会出现)。
在C++11中引入了原子变量atomic,通过原子变量可以实现一种更安全的懒汉模式的单例,代码如下:
#include<iostream>
#include<mutex>
#include<atomic>
using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{TaskQueue* taskQ = m_taskQ.load(); //取出来单例的值if (nullptr == taskQ){m_mutex.lock();taskQ = m_taskQ.load();if (nullptr == taskQ){cout << "我加了原子" << endl;taskQ = new TaskQueue;m_taskQ.store(taskQ); //保存到原子变量中}m_mutex.unlock();}return m_taskQ.load();}private:TaskQueue() = default; //无参构造static atomic<TaskQueue*>m_taskQ; //定义为原子变量static mutex m_mutex;
};
mutex TaskQueue::m_mutex;
atomic<TaskQueue*> TaskQueue::m_taskQ;int main()
{TaskQueue* obj = TaskQueue::getInstance();
}
使用局部静态
c++11新特性有如下规定:如果指令逻辑进入一个未被初始化的声明标量,所有并发执行应当等待该变量完成初始化。
#include<iostream>using namespace std;class TaskQueue
{
public:TaskQueue(const TaskQueue& obj) = delete;//禁用拷贝构造TaskQueue& operator = (const TaskQueue& obj) = delete;//禁用赋值构造static TaskQueue* getInstance() //获取单例的方法{static TaskQueue m_taskQ; //未被初始化return &m_taskQ;}void print(){cout << "hello, world!!!" << endl;}private:TaskQueue() = default; //无参构造
};int main()
{TaskQueue* obj = TaskQueue::getInstance();obj->print();
}
饿汉模式和懒汉模式的区别
懒汉模式的缺点是在创建实例对象的时候有安全问题,但这样可以减少内存的浪费(如果用不到就不去申请内存了)。饿汉模式则相反,在我们不需要这个实例对象的时候,它已经被创建出来,占用了一块内存。对于现在的计算机而言,内存容量都是足够大的,这个缺陷可以被无视。
相关文章:
【11】c++设计模式——>单例模式
单例模式是什么 在一个项目中,全局范围内,某个类的实例有且仅有一个(只能new一次),通过这个唯一的实例向其他模块提供数据的全局访问,这种模式就叫单例模式。单例模式的典型应用就是任务队列。 为什么要使…...
深度学习-卷积神经网络-AlexNET
文章目录 前言1.不同卷积神经网络模型的精度2.不同神经网络概述3.卷积神经网络-单通道4.卷积神经网络-多通道5.池化层6.全连接层7.网络架构8.Relu激活函数9.双GPU10.单GPU模型 1.LeNet-52.AlexNet1.架构2.局部响应归一化(VGG中取消了)3.重叠/不重叠池化4…...
人机关系不是物理关系也不是数理关系
人机关系是一种复杂的社会技术系统,涉及到人类和机器、环境之间的相互作用和影响。它不仅限于物理接触和数理规律,同时还包括了思维、情感、意愿等方面的交流和互动。在人机关系中,人类作为使用者和机器作为工具(将来可能会上升到…...
<html dir=ltr>是什么意思?
<html dirltr>的意思是: 文字默认从左到右排列 说明: HTML--超级文本标记语言 dir 属性 -- (文字的)排列方式属性 取值: ltr -- 代表左到右的排列方式 rtl -- 代表右到左的排列方式 默认值:ltr 示例: ltr左到右的对…...
工厂模式:简化对象创建的设计思想 (设计模式 四)
引言 在软件开发中,我们经常需要创建各种对象实例来满足不同的需求。通常情况下,我们会使用new关键字直接实例化对象,但这种方法存在一些问题,比如对象的创建逻辑分散在代码中,难以维护和扩展,同时也违反了…...
【2023最新】微信小程序中微信授权登录功能和退出登录功能实现讲解
文章目录 一、讲解视频二、小程序前端代码三、后端Java代码四、备注 一、讲解视频 教学视频地址: 视频地址 二、小程序前端代码 // pages/profile/profile.js import api from "../../utils/api"; import { myRequest } from "../../utils/reques…...
复习 --- C++运算符重载
.5 运算符重载 运算符重载概念:对已有的运算符重新进行定义,赋予其另外一种功能,以适应不同的数据类型 4.5.1 加号运算符重载 作用:实现两个自定义数据类型相加的运算 1 #include<iostream>2 using namespace std;3 /…...
复习 --- select并发服务器
selectIO多路复用并发服务器,是通过轮询检测文件描述符来实现并发 将内核要检测文件描述符放入集合中,调用select函数,通知内核区检测文件描述符集合中的文件描述符是否准备就绪,即对应的空间中是否有数据 对准备就绪的文件描述…...
程序三高的方法
程序三高的方法 目录概述需求: 设计思路实现思路分析1.1)高并发 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for change,c…...
全志ARM926 Melis2.0系统的开发指引⑦
全志ARM926 Melis2.0系统的开发指引⑦ 编写目的11. 调屏11.1. 调屏步骤简介11.1.1. 判断屏接口。11.1.2. 确定硬件连接。11.1.3. 配置显示部分 sys_config.fex11.1.3.1. 配置屏相关 IO 11.1.4. Lcd_panel_cfg.c 初始化文件中配置屏参数11.1.4.1. LCD_cfg_panel_info11.1.4.2. L…...
全志ARM926 Melis2.0系统的开发指引⑧
全志ARM926 Melis2.0系统的开发指引⑧ 编写目的12.5. 应用程序编写12.5.1. 简单应用编写12.5.1.1. 注册应用12.5.1.2. 创建管理窗口12.5.1.3. 实现管理窗口消息处理回调函数12.5.1.4. 创建图层12.5.1.5. 创建 framewin12.5.1.6. 实现 framewin 消息处理回调函数 -. 全志相关工具…...
区别对比表:阿里云轻量服务器和云服务器ECS对照表
阿里云轻量应用服务器和云服务器ECS区别对照表,一看就懂的适用人群、使用场景、优缺点、使用限制、计费方式、网路和镜像系统全方位对比,阿里云服务器网分享ECS和轻量应用服务器区别对照表: 目录 轻量应用服务器和云服务器ECS区别对照表 轻…...
【做题笔记】多项式/FFT/NTT
HDU1402 - A * B Problem Plus 题目链接 大数乘法是多项式的基础应用,其原理是将多项式 f ( x ) a 0 a 1 x a 2 x 2 a 3 x 3 ⋯ a n x n f(x)a_0a_1xa_2x^2a_3x^3\cdotsa_nx^n f(x)a0a1xa2x2a3x3⋯anxn中的 x 10 x10 x10,然后让大数的…...
网课搜题 小猿题库多接口微信小程序源码 自带流量主
多接口小猿题库等综合网课搜题微信小程序源码带流量主,网课搜题小程序, 可以开通流量主赚钱 搭建教程1, 微信公众平台注册自己的小程序2, 下载微信开发者工具和小程序的源码3, 上传代码到自己的小程序 源码下载:https://download.csdn.net/download/m0_…...
centos安装conda python3.10
最新版本的conda自带python3.10,直接安装即可。 手动创建一个conda文件夹,进入该文件夹,然后执行以下操作步骤。 1.下载 curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh2.安装 sh Miniconda3-latest-Linux-x86_64.…...
解密京东面试:如何应对Redis缓存穿透?
亲爱的小伙伴们,大家好!欢迎来到小米的微信公众号,今天我们要探讨一个在面试中可能会遇到的热门话题——Redis缓存穿透以及如何解决它。这个话题对于那些渴望进入技术领域的小伙伴们来说,可是必备的哦! 认识Redis缓存…...
#力扣:1. 两数之和@FDDLC
1. 两数之和 - 力扣(LeetCode) 一、Java import java.util.HashMap;class Solution {public int[] twoSum(int[] nums, int target) { //返回数组HashMap<Integer, Integer> map new HashMap<>(); //键:元素值;值&…...
【小沐学Python】各种Web服务器汇总(Python、Node.js、PHP、httpd、Nginx)
文章目录 1、Web服务器2、Python2.1 简介2.2 安装2.3 使用2.3.1 http.server(命令)2.3.2 socketserver2.3.3 flask2.3.4 fastapi 3、NodeJS3.1 简介3.2 安装3.3 使用3.3.1 http-server(命令)3.3.2 http3.3.3 express 4、PHP4.1 简…...
【AI视野·今日Robot 机器人论文速览 第四十六期】Tue, 3 Oct 2023
AI视野今日CS.Robotics 机器人学论文速览 Tue, 3 Oct 2023 Totally 76 papers 👉上期速览✈更多精彩请移步主页 Interesting: 📚Aerial Interaction with Tactile, 无人机与触觉的结合,实现空中交互与相互作用。(from CMU) website&#…...
macOS三种软件安装目录以及环境变量优先级
一、系统自带应用 这些软件(以git为例)位于根目录下的/usr/bin/xxx,又因为系统级环境变量文件/etc/paths已指定了命令查找位置: /usr/local/bin /System/Cryptexes/App/usr/bin /usr/bin /bin /usr/sbin /sbin所以这些自带应用可…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
