Qt下实现支持多线程的单例模式
Qt下实现支持多线程的单例模式
- Chapter1 Qt下实现支持多线程的单例模式($$$)
- 1. 代码介绍
- 2. 代码之路
- 3. 详细分析
- 3.1 什么是单例
- 3.2 如何让类无法实例化
- 3.3 如何调用这个唯一实例
- 3.4 如何支持多线程
- 3.5 如何解决内存泄漏
- 4. 结束语
- Chapter2 Qt 全局单例类
- Chapter3 Qt实用技巧:设计模式之单例模式,唯一实例类通用模板($$$)
- 需求
- 单例模式
- Qt单例模式示例模板(修复bug后的单例模式代码2:使用原子caozuo)
实现方式
1、实现单例
把类的构造函数、拷贝构造函数、赋值操作符定义为private的;
把获取单例的接口和唯一的实例指针定义为static的,不需要实例化,直接通过类名即可访问。
2、支持多线程
采用双重校验法,在获取单例的函数中使用互斥锁,确保不会出现两个线程同时new出这个单例类的实例化。
3、解决内存泄漏
析构单例指针,单独写一个类,利用这个类的析构函数来析构单例指针。
Chapter1 Qt下实现支持多线程的单例模式($$$)
原文链接:https://blog.csdn.net/lusanshui/article/details/84142869
1. 代码介绍
实现单例模式的代码很多。
本文的单例模式实现代码是本人一直在工程项目中使用的,现拿出和大家交流分享。
本文实现的单例模式,支持多线程,采用双重校验检索的方式,集成析构类,杜绝内存泄漏,稳定性好。 使用C++/Qt的朋友们可以了解一下。
不再废话,直接上代码。
2. 代码之路
头文件makelog.h
#include <QMutex>
#include <QObject>
class Makelog: public QObject
{Q_OBJECTpublic:static Makelog* getInstance(){if (m_pInstance == NULL){QMutexLocker mlocker(&m_Mutex); //双检索,支持多线程if (m_pInstance == NULL){m_pInstance = new Makelog();}}return m_pInstance;}private:Makelog(){}Makelog(const Makelog&){}Makelog& operator ==(const Makelog&){}static Makelog* m_pInstance; //类的指针static QMutex m_Mutex;public:class CGarbo //专用来析构m_pInstance指针的类{public:~CGarbo(){if (m_pInstance != NULL){delete m_pInstance;m_pInstance = NULL;}}};static CGarbo m_Garbo;
};
makelog.cpp文件
Makelog* Makelog::m_pInstance = NULL;
Makelog::CGarbo m_Garbo;
QMutex Makelog::m_Mutex;
支持多线程,无内存泄漏的单例模式就实现了。
下面举例说明具体的使用:
在头文件中加入一个public函数、一个public变量。
public:
void readFile();
QString m_config;
在源文件中加入函数具体实现,使得readFile()或m_config有实际的意义。
那么,在一个工程内的其他类中,只需做两个步骤,就可以使用这个readFile()函数和m_config变量了。
步骤1:包含头文件
#include “makefile.h”
步骤2:通过单例类入口调用函数或变量
Makelog::getInstance()->readFile(); //使用函数
Makelog::getInstance()->m_config; //使用变量
3. 详细分析
3.1 什么是单例
单例是一种软件设计模式,采用单例模式书写的类可以确保在一个工程中只有一个对象实例。再通俗点,就是一个类写好了之后,就不需要也无法再把这个类实例化了,因为写这个类的时候已经确保了有且仅有一个已经实例化的对象。
这样不是很蠢么?花了这么多功夫写了一个类,你告诉我这个类没法用来new出对象了?那我怎么使用这个类?我写个配合静态变量的静态函数,使用起来不是更方便?
当然不蠢,非但不蠢,而且单例模式是所有设计模式中使用最为频繁的一个设计模式。没法new出对象,因为单例模式已经帮你new了一个对象,而且让你的工程中只有这个对象了;使用这个对象只需要包含头文件,然后调用接口指针函数就可以了;静态的全局函数或变量代码实现起来方便,但是不具有类的封装性和灵活性。
3.2 如何让类无法实例化
首先要清楚类实例化无非就是三种方式:
1)采用构造函数实例化;
2)用拷贝函数实现实例化;
3)赋值操作实现实例化。
所以,只需要把这个类的构造函数、拷贝函数、赋值操作写成私有的,就无法调用这些函数,自然就无法实例化了。
正如上文所示的几个函数:
private:
Makelog(){} //构造函数
Makelog(const Makelog&){} //拷贝函数
Makelog& operator ==(const Makelog&){} //赋值操作符重写
当然,如果有一些初始化的操作,也可以写在私有构造函数的双括号内。
3.3 如何调用这个唯一实例
广泛采用的做法就是在写一个public的函数作为接口,这个函数返回单例类唯一实例的指针。
最简单的写法如下:
Makelog* getInstance()
{
if (m_pInstance == NULL)
{
m_pInstance = new Makelog(); //调用private构造函数,把唯一实例的指针实例化
}
return m_pInstance;
}
Makelog* m_pInstance; //唯一实例的指针
这个写法看着真不错,可是这么写遇到了一个小小的悖论。
“我如何去调用执行这个getInstance()函数啊,对,我需要一个实例化对象才能去执行!那我去new个对象,等等,唯一的实例化对象是通过这个函数才能找到的啊!”
有方法解决么,当然,类的静态方法不需要实例化对象,用
类名::方法()
的形式就可以调用执行了,所以把getInstance()函数前加一个static就好了。
但是静态方法只能使用静态成员变量啊,那就把唯一实例的指针m_pInstance也变成static的吧。
Ok,这样有没有隐患啊?隐患?static类型的instance存在静态存储区,每次调用时,都指向的同一个对象。非但没有隐患,简直堪称完美!
现在上面的代码就变成这样:
public:
static Makelog* getInstance()
{
if (m_pInstance == NULL )
{
m_pInstance = new Makelog(); //用私有构造函数new出了这个类的唯一对象
}
return m_pInstance;
}
private:
static Makelog* m_pInstance; //唯一实例指针
这样写完全没有问题,但是不支持多线程的调用。因为new Makelog()需要时间,所以当两个线程同时判断m_pInstance ==NULL,同时执行了m_pInstance = new Makelog()这句代码,问题就大了。
3.4 如何支持多线程
为了解决3.3节产生的bug,广泛采用的方式是双重校验检索的方法。
就是利用互斥锁(用来保证锁内代码最多只有一个线程在同时执行)的方式,确保不会出现两个线程同时new出这个单例类的唯一实例的情况发生。
具体代码如下:
static Makelog* getInstance()
{
if (m_pInstance == NULL)
{
QMutexLocker mlocker(&m_Mutex); //加锁,锁内代码只有一个线程执行
if (m_pInstance == NULL) //先执行的线程会进入内部new对象,后一个线程判断m_pInstance就不是NULL了
{
m_pInstance = new Makelog();
}
}
return m_pInstance;
}
至此双重校验检索解决多线程问题的单例问题就解决了。当然还可以用原子锁的方法来解决,但是灵活性不强(也可能是我太外行,灵活不起来—。—),这里就不介绍了。
3.5 如何解决内存泄漏
解决单例类的内存析构主要就是解决static Makelog* m_pInstance这个指针的析构问题(毕竟其他的可以不用指针的嘛)。我总结觉得写一个专门用来析构的类是最方便有效和无脑的方法了,推荐给大家。
具体就是在单例类中写一个类:
public:
class CGarbo //专用来析构m_pInstance指针的类
{
public:
~CGarbo() //这个类只有析构函数
{
if (m_pInstance != NULL)
{
delete m_pInstance;
m_pInstance = NULL;
}
}
};
static CGarbo m_Garbo; //声明一个静态的对象
然后在cpp文件中声明一下 Makelog::CGarbo m_Garbo就可以了。
这个类只有析构函数,析构函数的作用就是delete单例唯一对象的指针。
析构类声明一个static对象,因为静态对象系统会在关闭程序时自动析构,就可以执行到析构函数内部的代码了。
4. 结束语
单例模式是非常常用而基础的一个设计模式,本文作者第一次写博客,有不详或错误之处还请大家指正。对于还在使用C++/Qt的初学者,请不要因为害怕而不去深究和掌握单例模式这个好用实用的工具。
Chapter2 Qt 全局单例类
原文链接:https://blog.csdn.net/z_ujmn/article/details/105299528
单例模式:将构造函数私有,能够禁止类外生成对象。将拷贝构造函数和赋值操作符重载函数声明为delete,以防生成的对象被复制。同时声明一个静态函数和静态互斥锁。静态函数用来生成对象,注意,静态函数不需要通过对象去调用。互斥锁也声明为静态是因为在静态函数里是不能访问类成员变量的,因为静态函数不需要通过对象调用,如果可以访问类成员变量,它自己也不知道访问的是哪个。
#ifndef APPEVENT_H
#define APPEVENT_H#include <QObject>
#include <QMutex>
#include <QSharedPointor>class AppEvent : public QObject
{Q_OBJECT
public:static QSharedPointer<AppEvent> getInstance();private:
explicit AppEvent(QObject *parent = 0);AppEvent& operator =(AppEvent&) = delete;AppEvent(AppEvent&) = delete;
private:static QSharedPointer<AppEvent> m_appEvent_ptr;static QMutex m_mutex;
signals:public slots:
};#endif //APPEVENT_H
静态函数加锁前后分别判断一次可以提高效率。互斥锁以及判断对象是否被创建都保证只会存在一个对象。
#include "appevent.h"AppEvent::AppEvent(QObject *parent)
:QObject(parent)
{}QSharedPointer<AppEvent> AppEvent::m_appEvent_ptr;
QMutex AppEvent::m_mutex;QSharedPointer<AppEvent> AppEvent::getInstance()
{if (nullptr == m_appEvent_ptr){m_mutex.lock();if (nullptr == m_appEvent_ptr){m_appEvent_ptr = QSharedPointer<AppEvent>(new AppEvent);}m_mutex.unlock();}return m_appEvent_ptr;
}
其它文件内引用该全局单例对象时,加上单例对象文件的头文件,声明时带上extern。注意,全局单例对象只可初始化一次。
#include <QApplication>
QSharedPointer<AppEvent> g_appEvent_ptr = AppEvent::getInstace();int main(int argc, char*argv[])
{QApplication a(argc, argv);a.exec();
}
Chapter3 Qt实用技巧:设计模式之单例模式,唯一实例类通用模板($$$)
原文链接:https://blog.csdn.net/qq21497936/article/details/80046081
需求
Qt常需要一个类,全局调用,是设计模式中的单例模式。
单例模式
单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
显然单例模式的要点有三个;
一是某个类只能有一个实例;
二是它必须自行创建这个实例;
三是它必须自行向整个系统提供这个实例。
Qt单例模式示例模板(此版本重大bug)
使用DbService::instance()全局获取该对象
头文件
#ifndef DBSERVICE_H
#define DBSERVICE_H#include <QObject>
#include <QMutex>
#include <QMutexLocker>class DbService : public QObject
{Q_OBJECT
public:explicit DbService(QObject *parent = 0);public:static DbService * instance();signals:public slots:protected:private:static DbService *_pInstance;static QMutex _mutex;
};#endif // DBSERVICE_H
源文件(存在bug)
#include "DbService.h"DbService * DbService::_pInstance = 0;
QMutex DbService::_mutex;DbService::DbService(QObject *parent) : QObject(parent)
{}DbService * DbService::instance()
{if(!_pInstance){QMutexLocker lock(&_mutex);if(!_pInstance){_pInstance = new DbService();}}return _pInstance;
}
bug(感谢网友大神:火龙 的帮助)
_pInstance = new DbService();
- 申请DbService的内存
- 在申请的内存上构造DbService
- 将_pInstance指针指向这个内存
这个new有这么三步
编译器可能是132这么执行的,多个线程第一次同时使用时,可能出现野指针,即编译器先指向内存(准备第三步构造)时,另一个线程获取,则出现野指针,运行出现段错误。
源文件(修复完bug)
#include "DbService.h"DbService * DbService::_pInstance = 0;
QMutex DbService::_mutex;DbService::DbService(QObject *parent) : QObject(parent)
{}DbService * DbService::instance()
{if(!_pInstance){QMutexLocker lock(&_mutex);if(!_pInstance){DbService *pInstance = new DbService(); // 修改处_pInstance = pInstance; // 修改处}}return _pInstance;
}
Qt单例模式示例模板(修复bug后的单例模式代码2:使用原子caozuo)
头文件
#ifndef DBSERVICE_H
#define DBSERVICE_H#include <QObject>
#include <QMutex>
#include <QMutexLocker>class DbService : public QObject
{Q_OBJECT
public:explicit DbService(QObject *parent = 0);public:static DbService *getInstance();signals:public slots:protected:private:static QAtomicPointer<DbService> _instance;static QMutex _mutex;
};#endif // DBSERVICE_H
源文件
#include "DbService.h"QAtomicPointer<DbService> DbService::_instance = 0;
QMutex DbService::_mutex;DbService::DbService(QObject *parent) : QObject(parent)
{}DbService * DbService::instance()
{
#ifndef Q_ATOMIC_POINTER_TEST_AND_SET_IS_ALWAYS_NATIVEif(!QAtomicPointer::isTestAndSetNative())//运行时检测qDebug() << "Error: TestAndSetNative not supported!";
#endif//使用双重检测。/*! testAndSetOrders操作保证在原子操作前和后的的内存访问* 不会被重新排序。*/if(_instance.testAndSetOrdered(0, 0))//第一次检测{QMutexLocker locker(&mutex);//加互斥锁。_instance.testAndSetOrdered(0, new DbService);//第二次检测。}return _instance;
}
相关文章:
Qt下实现支持多线程的单例模式
Qt下实现支持多线程的单例模式 Chapter1 Qt下实现支持多线程的单例模式($$$)1. 代码介绍2. 代码之路3. 详细分析3.1 什么是单例3.2 如何让类无法实例化3.3 如何调用这个唯一实例3.4 如何支持多线程3.5 如何解决内存泄漏 4. 结束语 Chapter2 Qt 全局单例类Chapter3 Qt实用技巧&a…...

基于Java的宠物商店管理系统设计与实现(源码+lw+部署文档+讲解等)
文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序(小蔡coding) 代码参考数据库参考源码获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作者&am…...

ArcGIS中批量mxd高版本转低版本
我们经常在给别人发ArcGIS的工程文件mxd,结果到别人那发现mxd工程文件打不开,原因是我们的arcgis版本高于别人,此时工程文件又很多,一个个转存成低版本又嫌麻烦,于是我们做了个批量mxd高版本转低版本的小工具ÿ…...

三篇论文:速览GPT在网络安全最新论文中的应用案例
GPT在网络安全领域的应用案例 写在最前面论文1:Chatgpt/CodeX引入会话式 APR 范例利用验证反馈LLM 的长期上下文窗口:更智能的反馈机制、更有效的信息合并策略、更复杂的模型结构、鼓励生成多样性和GPT类似的步骤:Conversational APR 对话式A…...

美术如何创建 skybox 贴图资源?
文章目录 目的PS手绘Panorama To CubemapPS手绘Pano2VRSkybox & Cubemap Tutorial (Maya & Photoshop)Unity 中使用 ReflectionProbe 生成 Cubemap 然后再 PS 调整PS直接手绘 cubemapBlender 导入 Panorama,然后烘焙到 cubemap,再导入unity中使用…...
【Linux 用户,用户组管理】
文章目录 什么是Linux用户和用户组用户(User)用户组(User Group) Linux用户和用户组管理命令1. 创建用户2. 删除用户3. 修改用户信息4. 创建用户组5. 将用户添加到用户组6. 用户和用户组的查询 用户和用户组管理实战 什么是Linux用…...

VS2022 C# 读取 excel 2023年
今天是2023年6月26日,我有一个excel表要读数据,然后放到winform程序来处理,网上的资料太旧,很多用不起来,试了一个可以使用,记录一下: 一、excel文件后缀需要小写。 二、用VS2022建一个winform…...

C# | Chaikin算法 —— 计算折线对应的平滑曲线坐标点
Chaikin算法——计算折线对应的平滑曲线坐标点 本文将介绍一种计算折线对应的平滑曲线坐标点的算法。该算法使用Chaikin曲线平滑处理的方法,通过控制张力因子和迭代次数来调整曲线的平滑程度和精度。通过对原始点集合进行切割和插值操作,得到平滑的曲线坐…...
day44
什么是前端 前端是所有跟用户直接打交道的都可以称之为是前端 比如:pc页面、手机页面、平板页面、汽车显示屏等等显示出来的都是前端内容 什么是后端? 就是一堆代码,用户不能够直接看到,不直接与用户打交道的都是后端 常见的后端…...
python常用操作汇总
python创建二维数组 python创建三行三列的二维数组,下面方法是错误的,因为是浅拷贝: lst1 [0] * 3 lst2 [lst1] * 3 lst2[1][1] 2 print(lst2) # [[0, 2, 0], [0, 2, 0], [0, 2, 0]]正确姿势 lst [[0 for j in range(3)] for i in ran…...

赴日IT培训 日本IT行业为啥吃香?
确实现在有许多小伙伴尝到了赴日IT的甜头,可是去日本从事IT行业真的很简单吗?为什么日本的IT行业这么缺人呢?那今天小编就跟大家聊一聊日本的IT行业。 咱们先来说说日本的IT行业为什么缺人?其实不只是IT行业,可以说日…...

2016年亚太杯APMCM数学建模大赛A题基于光学信息数据的温度及关键元素含量预测求解全过程文档及程序
2016年亚太杯APMCM数学建模大赛 A题 基于光学信息数据的温度及关键元素含量预测 原题再现 光含有能量,在一定条件下可以转化为热。燃烧是一种常见的现象,既能发光又能发热。光和热通常是同时存在的,一般来说,光强度越高…...

一文讲明:企业知识库的作用和搭建方法
在现代商务环境中,企业面临着大量的信息和知识流动。这些信息和知识散落在各个部门、团队甚至个人之间,难以进行有效的整合和利用。而企业知识库的出现解决了这一问题。它提供了一个统一的平台,将分散的信息汇聚到一个集中的数据库中…...
技术的新浪潮:从SOCKS5代理到跨界电商的未来
在当今这个日新月异的技术时代,各种创新技术如雨后春笋般涌现。从SOCKS5代理到跨界电商,再到爬虫技术、出海战略和游戏产业的飞速发展,我们正处于一个技术变革的黄金时代。 SOCKS5代理:安全的网络通道 SOCKS5代理是一种网络协议…...

Android intent的一些小使用
目录: 1. Test5.java2. activity_main5.xml3. Empty.java (这个是用来带参数打开Activity按钮用的)4. activity_empty.xml5. 总结 一些基本的问题就不进行说明了,直接上代码!!! // 最后的隐形intent和带返回值没有解决…...
Android 关闭 SELinux 释放权限限制
Android 关闭 SELinux 释放权限限制 接前一篇,后续收到客户需求想要关闭 SELinux 放开安全权限,SELinux 是 Linux 的一个安全子系统,SELinux 主要作用是最大限度地减小系统中服务进程可访问的资源,我们想要放开安全权限只需处理 …...

国际腾讯云自主拼装直播 URL教程!!!
注意事项 创建转码模板 并与播放域名进行 绑定 后,转码配置后的直播流,需将播放地址的 StreamName 拼接为 StreamName_转码模板名称,更多详情请参见 播放配置。 前提条件 已注册腾讯云账号,并开通 腾讯云直播服务。 已在 域名…...

AQS 为什么要使用双向链表?
双向链表 双向链表的特点是它有两个指针,一个指针指向前置节点,一个指针指向后继节点。所以,双向链表可以支持 常量 O(1) 时间复杂度的情况下找到前驱结点,基于这样的特点。双向链表在插入和删除操作的时候,要比单向链…...

Golang 自定义函数库(个人笔记)
1.用字符串连接切片元素(类似php implode) package mainimport ("fmt""strconv""strings" )func main() {data : []int{104, 101, 108, 108, 111}fmt.Println(IntSliceToString(data, ",")) }func IntSliceToS…...
openlayers 地图组件封装
openlayers 地图组件封装 <template><div class"mapBox"><!-- 地图 --><div ref"map" id"map" class"map"></div><!-- 点位弹窗 --><div id"popup" class"ol-popup">…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...

莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...
boost::filesystem::path文件路径使用详解和示例
boost::filesystem::path 是 Boost 库中用于跨平台操作文件路径的类,封装了路径的拼接、分割、提取、判断等常用功能。下面是对它的使用详解,包括常用接口与完整示例。 1. 引入头文件与命名空间 #include <boost/filesystem.hpp> namespace fs b…...

Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...