十三、Qt多线程与线程安全
一、多线程程序
QThread类提供了管理线程的方法:
- 一个对象管理一个线程
- 一般从QThread继承一个自定义类,重载run函数
1、实现程序

(1)创建项目,基于QDialog
(2)添加类,修改基于QThread

#ifndef DICETHREAD_H
#define DICETHREAD_H#include <QThread>class DiceThread : public QThread
{Q_OBJECTprivate:int m_seq = 0;int m_diceValue;bool m_Paused = true;bool m_stop = false;public:explicit DiceThread();void diceBegin();void dicePause();void stopThread();protected:void run() Q_DECL_OVERRIDE;signals:void newValued(int seq, int diceValue);public slots:
};#endif // DICETHREAD_H
#include "dicethread.h"
#include <QTime>DiceThread::DiceThread()
{}void DiceThread::diceBegin()
{m_Paused = false;
}void DiceThread::dicePause()
{m_Paused = true;
}void DiceThread::stopThread()
{m_stop = true;
}void DiceThread::run()
{m_stop = false;m_seq = 0;qsrand(QTime::currentTime().second());while (!m_stop) {if(!m_Paused){m_diceValue = qrand()%6+1;m_seq++;emit newValued(m_seq, m_diceValue);}sleep(1);}quit();
}
(3)实现按钮功能
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{ui->setupUi(this);ui->btnStartThread->setEnabled(true);ui->btnStart->setEnabled(false);ui->btnStop->setEnabled(false);ui->btnStopThread->setEnabled(false);connect(&threadA, SIGNAL(started()),this, SLOT(on_threadAStarted()));connect(&threadA, SIGNAL(finished()),this, SLOT(on_threadAFinished()));connect(&threadA, SIGNAL(newValued(int,int)),this, SLOT(on_threadAnewValue(int,int)));
}Dialog::~Dialog()
{delete ui;
}void Dialog::closeEvent(QCloseEvent *event)
{if(threadA.isRunning()){threadA.stopThread();threadA.wait();}event->accept();
}void Dialog::on_btnStartThread_clicked()
{threadA.start();
}void Dialog::on_btnStart_clicked()
{threadA.diceBegin();
}void Dialog::on_btnStop_clicked()
{threadA.dicePause();
}void Dialog::on_btnStopThread_clicked()
{threadA.stopThread();
}void Dialog::on_btnClearText_clicked()
{ui->plainTextEdit->clear();
}void Dialog::on_threadAnewValue(int seq, int diceValue)
{ui->plainTextEdit->appendPlainText(QString::asprintf("第%d次投色子: 点数%d", seq, diceValue));
}void Dialog::on_threadAStarted()
{ui->labelStatus->setText("Thread状态:started");ui->btnStartThread->setEnabled(false);ui->btnStart->setEnabled(true);ui->btnStop->setEnabled(true);ui->btnStopThread->setEnabled(true);
}void Dialog::on_threadAFinished()
{ui->labelStatus->setText("Thread状态:finished");ui->btnStartThread->setEnabled(true);ui->btnStart->setEnabled(false);ui->btnStop->setEnabled(false);ui->btnStopThread->setEnabled(false);
}

二、互斥量
QMutex和QMutexLocker是基于互斥量的线程同步类
- QMutex定义的实力是互斥量,主要提供了三个函数
- lock():锁定互斥量,如果另一个线程锁定了这个互斥量,将阻塞直到另一个解锁
- unlock():解锁一个互斥量
- trylock():尝试锁定一个互斥量,如果成功返回true,失败(其他线程已经锁定这个互斥量)返回false,不阻塞线程。
- QMutexLocker简化了互斥量的处理
- 构造一个函数接受一个互斥量作为参数,并将其锁定
- 析构函数解锁该互斥量
1、实现程序
(1)拷贝上一个项目
(2)修改程序为直接读取
void DiceThread::readValue(int *seq, int *diceValue)
{*seq = m_seq;*diceValue = m_diceValue;
}void DiceThread::run()
{m_stop = false;m_seq = 0;qsrand(QTime::currentTime().second());while (!m_stop) {if(!m_Paused){m_diceValue = 50;msleep(50);m_diceValue = qrand();msleep(50);m_diceValue = m_diceValue%6+1;msleep(50);m_seq++;
// emit newValued(m_seq, m_diceValue);}sleep(1);}quit();
}
void Dialog::on_TimerOut()
{int seq, diceValue;threadA.readValue(&seq, &diceValue);ui->plainTextEdit->appendPlainText(QString::asprintf("第%d次投色子: 点数%d", seq, diceValue));
}

(3)使用QMutex互斥量
void DiceThread::readValue(int *seq, int *diceValue)
{mMutex.lock();*seq = m_seq;*diceValue = m_diceValue;mMutex.unlock();
}void DiceThread::run()
{m_stop = false;m_seq = 0;qsrand(QTime::currentTime().second());while (!m_stop){if(!m_Paused){mMutex.lock();m_diceValue = 50;msleep(50);m_diceValue = qrand();msleep(50);m_diceValue = m_diceValue % 6 + 1;msleep(50);m_seq++;// emit newValued(m_seq, m_diceValue);mMutex.unlock();}sleep(1);}quit();
}

(4)使用QMutexLocker
void DiceThread::readValue(int *seq, int *diceValue)
{QMutexLocker locker(&mMutex);*seq = m_seq;*diceValue = m_diceValue;
}
(5)使用QMutex.trylock
bool DiceThread::readValue(int *seq, int *diceValue)
{// QMutexLocker locker(&mMutex);if(mMutex.tryLock()){*seq = m_seq;*diceValue = m_diceValue;mMutex.unlock();return true;}return false;
}
三、读写锁
QReadWriteLock提供了以下主要函数:
- lockForRead():只读方式锁定资源,如果有其他线程以写入方式锁定,这个函数会阻塞
- lockForWrite():以写入方式锁定资源,如果本线程或者其他线程以读取或写入锁定资源,则函数阻塞
- unlock():解锁
- tryLockForRead():是lockForRead非阻塞版本
- tryLockForWrite():是lockForWrite非阻塞版本
- 读写锁同样有QReadLocker和QWriteLocker
四、条件变量QWaitCondition
QWaitCondition用于通知其他线程,如接收数据和处理数据之间通知。提供了一些函数:
- wait(QMutex *lockedMutex):进入等待状态,解锁互斥量lockMutex,被唤醒后锁定lockMutex并退出函数
- wakeAll():唤醒所有处于等待的线程,线程唤醒的顺序不确定,有操作系统调度策略决定
- QakeOne():唤醒一个处于等待状态的线程,唤醒哪个线程不确定,由操作系统调度策略决定
1、实现程序

(1)拷贝上一个项目
(2)使用QWaitCondition设置数据更新
#include "dicethread.h"
#include <QTime>
#include <QWaitCondition>
#include <QMutex>int m_seq = 0;
int m_diceValue;
bool m_stop = false;
QMutex m_Mutex;
QWaitCondition waitCondition;ProducerThread::ProducerThread()
{}void ProducerThread::stopThread()
{m_stop = true;
}void ProducerThread::run()
{m_stop = false;m_seq = 0;qsrand(QTime::currentTime().second());while (!m_stop){m_Mutex.lock();m_diceValue = qrand() % 6 + 1;m_seq++;m_Mutex.unlock();waitCondition.wakeOne();sleep(1);}quit();
}ConsumerThread::ConsumerThread()
{}void ConsumerThread::stopThread()
{m_stop = true;waitCondition.wakeOne(); // 需要给wait置信号,否则阻塞无法结束
}void ConsumerThread::run()
{m_stop = false;while (!m_stop){m_Mutex.lock();waitCondition.wait(&m_Mutex);emit newValued(m_seq, m_diceValue);m_Mutex.unlock();msleep(100);}quit();
}

五、信号量
QSemaphore信号量通常用于保护一定数量的相同的资源。QSemaphore是实现信号量功能的类,提供了以下函数:
- acquire(int n):尝试获得n个资源,如果不够将阻塞线程,直到n个资源可用
- release(int n):释放资源,如果资源已经全部可用,则可扩充资源总数
- int available():返回房前信号量的资源个数
- bool tryAcquire(int n=1):尝试获取n个资源,不成功是,不阻塞线程
1、实现程序

(1)创建项目,基于QDIalog

(2)创建线程类
(3)使用信号量实现功能
#include "threadtest.h"
#include <QSemaphore>const int bufferSize = 8;
int buffer1[bufferSize] = {0};
int buffer2[bufferSize] = {0};
int curBuf = 1; // 当前采集数据使用的缓冲区QSemaphore semEmptyBufs(2); // 两个资源
QSemaphore semFullBufs;ThreadDAQ::ThreadDAQ()
{}void ThreadDAQ::stopThread()
{m_stop = true;
}void ThreadDAQ::run()
{m_stop = false;int counter = 0;while(!m_stop){semEmptyBufs.acquire();for (int i = 0; i < bufferSize; ++i){if(curBuf == 1){buffer1[i] = counter;}else{buffer2[i] = counter;}counter++;msleep(50);}if(curBuf == 1){curBuf = 2;}else{curBuf = 1;}semFullBufs.release();}exit();
}ThreadShow::ThreadShow()
{}void ThreadShow::stopThread()
{m_stop = true;
}void ThreadShow::run()
{m_stop = false;int seq = 0;while(!m_stop){semFullBufs.acquire();int buf[bufferSize] = {0};if(curBuf == 1){memcpy(buf, buffer2, sizeof(int)*bufferSize);}else{memcpy(buf, buffer1, sizeof(int)*bufferSize);}emit newValue(buf, bufferSize, seq++);semEmptyBufs.release();}exit();
}
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :QDialog(parent),ui(new Ui::Dialog)
{ui->setupUi(this);ui->btnStopThread->setEnabled(false);connect(&threadConsumer, SIGNAL(newValue(int*, int, int)),this, SLOT(on_threadNewValue(int*, int, int)));connect(&threadProducer, SIGNAL(started()),this, SLOT(on_threadProducer_started()));connect(&threadProducer, SIGNAL(finished()),this, SLOT(on_threadProducer_finished()));connect(&threadConsumer, SIGNAL(started()),this, SLOT(on_threadConsumer_started()));connect(&threadConsumer, SIGNAL(finished()),this, SLOT(on_threadConsumer_finished()));
}Dialog::~Dialog()
{delete ui;
}void Dialog::on_threadNewValue(int *data, int count, int seq)
{QString str = QString::asprintf("第%03d次,内容:", seq);for (int var = 0; var < count; ++var){str += QString::asprintf("%03d ,", data[var]);}ui->plainTextEdit->appendPlainText(str);
}void Dialog::on_btnStartThread_clicked()
{threadConsumer.start();threadProducer.start();ui->btnStartThread->setEnabled(false);ui->btnStopThread->setEnabled(true);
}void Dialog::on_btnStopThread_clicked()
{threadProducer.stopThread();threadConsumer.stopThread();ui->btnStartThread->setEnabled(true);ui->btnStopThread->setEnabled(false);
}void Dialog::on_btnClearText_clicked()
{ui->plainTextEdit->clear();
}void Dialog::on_threadProducer_started()
{ui->labelProducer->setText("Producer线程:started");
}void Dialog::on_threadProducer_finished()
{ui->labelProducer->setText("Producer线程:finished");
}void Dialog::on_threadConsumer_started()
{ui->labelConsumer->setText("Consumer线程:started");
}void Dialog::on_threadConsumer_finished()
{ui->labelConsumer->setText("Consumer线程:finished");
}

相关文章:
十三、Qt多线程与线程安全
一、多线程程序 QThread类提供了管理线程的方法:一个对象管理一个线程一般从QThread继承一个自定义类,重载run函数 1、实现程序 (1)创建项目,基于QDialog (2)添加类,修改基于QThr…...
今日话题:---自卑
自卑是一种普遍存在的心理现象,它可能源于个人对自身能力、外貌、社会地位等方面的不满意或不自信。自卑感可能会导致消极的情绪和行为,如焦虑、抑郁、逃避现实等。然而,适度的自卑感也可能激发个人努力提升自己,从而实现自我成长…...
Unity 预制体与变体
预制体作用: 更改预制体,则更改全部的以预制体复制出的模型。 生成预制体: 当你建立好了一个模型,从层级拖动到项目中即可生成预制体。 预制体复制模型: 将项目中的预制体拖动到层级中即可复制。或者选择物体复制粘贴。…...
leetcode:860.柠檬水找零
题意:按照支付顺序,进行支付,能够正确找零。 解题思路:贪心策略:针对支付20的客人,优先选择消耗10而不是消耗5,因为5可以用来找零10或20. 代码实现:有三种情况(代表三种…...
Python程序的流程
归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言📝 年轻是我们唯一拥有权利去编制梦想的时…...
C语言可以干些什么?C语言主要涉及哪些IT领域?
C语言可以干些什么?C语言主要涉及哪些IT领域? 在开始前我有一些资料,是我根据网友给的问题精心整理了一份「C语言的资料从专业入门到高级教程」, 点个关注在评论区回复“888”之后私信回复“888”,全部无偿共享给大家…...
element-ui附件上传及在线查看详细总结,后续赋源码
一、附件上传 1、在element-ui上面复制相应代码 a、accept"image/*,.pdf,.docx,.xlsx,.doc,.xls" 是规定上传文件的类型,若是不限制,可以直接将accept‘all即可; b、:action"action" 这个属性就是你的上传附件的地址&am…...
投标中excel表格常用功能梳理
投标中excel表格常用功能梳理: 1.投标报价调整报价的办法: 目的调整报价,把“红框”的报价增加30%,50% 增加30%的步骤: 步骤1:选择1.3 复制(ctrlc) 步骤2:选择性黏贴 …...
C++二叉搜树的实现(递归和非递归)
目录 1.什么是二叉搜索树 2.二叉搜索树的查找 3.二叉搜索树插入 4.二叉搜索树的删除 1.删除的节点只有左子树或者右子树 2.删除节点左右子树都有的情况 5.代码 1.什么是二叉搜索树 左节点的值小于根节点 右节点大于根节点 左右子树也满足上面两个条件 例:…...
蓝桥杯算法 一.
分析: 本题记录:m个数,异或运算和为0,则相加为偶数,后手获胜。 分析: 369*99<36500,369*100>36500。 注意:前缀和和后缀和问题...
如何学习自然语言处理之语言模型
自然语言处理(NLP)是一种人工智能技术,它使计算机能够理解和处理人类语言。而语言模型是NLP中的一个重要概念,主要是用来估测一些词的序列的概率,即预测p(w1, w2, w3 … wn),其中一个应用就是句子的生成。 …...
Zoho ToDo 满足您的需求:任务管理满足隐私和安全要求
任务管理工具已经成为我们日常生活中不可或缺的一部分,它们帮助我们处理各种事务,从杂项和愿望清单到管理截止日期和资源。这些工具不仅仅是简单的任务列表,它们掌握了项目的蓝图、雄心勃勃的目标和完成的最后期限。然而随着这些工具的使用越…...
仿牛客网项目---社区首页的开发实现
从今天开始我们来写一个新项目,这个项目是一个完整的校园论坛的项目。主要功能模块:用户登录注册,帖子发布和热帖排行,点赞关注,发送私信,消息通知,社区搜索等。这篇文章我们先试着写一下用户的…...
虚拟机部署Sentry步骤,国内地址
Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC 👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资源分…...
[Android View] 可绘制形状 (Shape Xml)
一切以官方文档为主 官方文档https://developer.android.com/guide/topics/resources/drawable-resource?hlzh-cn#Shape 什么是可绘制形状 可以理解为用xml文件来描述一个简单的Drawable图形,比如说以下这段xml就可以用来描述一个白色的圆形: <?…...
[游戏开发][虚幻5]新建项目注意事项
鼠标右键点击Client.uproject文件,可以看到三个比较关键的选项, 启动游戏,生成sln解决方案,切换引擎版本 断点调试 C代码重要步骤 如果你想断点调试C代码,则必须使用使用代码编译启动引擎,你需要做几个操作…...
防考试作弊切屏
防考试作弊切屏 方法一:监听页面失焦聚焦事件:防止任何操作 监听考试页面失焦事件记录切出时间页面聚焦时累积记录切入时间,累积时间大于1分钟自动交卷并移除时间页面销毁移出事件***bug:必须把事件回调定义为方法,在…...
浅析能耗监测系统在大型数据中心的应用
彭姝麟 Acrelpsl 1总体设计 大型数据中心能耗监测系统包含硬件和软件两大部分,其硬件组成主要包括监控服务器、主机设备、网络设备、环境参数传感器、通风模块等,总体采集逻辑采用三级监控体系。一级为主机设备,作为系统的应用层,…...
robotframework-去除字符串左侧的0的方法
参考文章:https://www.cnblogs.com/xiaodouzhou-123/p/10333759.html...
【Linux C | 网络编程】getaddrinfo 函数详解及C语言例子
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
结构化文件管理实战:实现目录自动创建与归类
手动操作容易因疲劳或疏忽导致命名错误、路径混乱等问题,进而引发后续程序异常。使用工具进行标准化操作,能有效降低出错概率。 需要快速整理大量文件的技术用户而言,这款工具提供了一种轻便高效的解决方案。程序体积仅有 156KB,…...
初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)
零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...
比较数据迁移后MySQL数据库和ClickHouse数据仓库中的表
设计一个MySQL数据库和Clickhouse数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
篇章一 论坛系统——前置知识
目录 1.软件开发 1.1 软件的生命周期 1.2 面向对象 1.3 CS、BS架构 1.CS架构编辑 2.BS架构 1.4 软件需求 1.需求分类 2.需求获取 1.5 需求分析 1. 工作内容 1.6 面向对象分析 1.OOA的任务 2.统一建模语言UML 3. 用例模型 3.1 用例图的元素 3.2 建立用例模型 …...
