系统讨论Qt的并发编程2——介绍一下Qt并发的一些常用的东西
目录
QThreadPool与QRunnable
互斥机制:QMutex, QMutexLocker, QSemaphore, QWaitCondition
跨线程的通信
入门QtConcurrent,Qt集成的一个并发框架
一些参考
QThreadPool与QRunnable
QThreadPool自身预备了一些QThread。这样,我们就不需要频繁的创建和销毁我们的QThread,从而提升了性能。每个 Qt 应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问。
那么,我们应该如何启动QThreadPool呢?答案是——使用一个接口叫做QRunnable。QRunnable自身只是要求你实现一个接口。那就是run接口。QRunnable自身不是一个QObject,意味着他是没有信号与槽的,适合那些跟Qt关系不大的,或者说跨线程之间没有对象交互的场景。
#include "Counter.h"
#include <QDebug>
#include <QRandomGenerator>
#include <QThread>
Counter::Counter() {
}
void Counter::run() {qInfo() << "Starting the job!";for (int i = 0; i < 20; i++) {qInfo() << QThread::currentThread() << "" << i;auto sleep_time = QRandomGenerator::global()->bounded(1, 100);QThread::msleep(sleep_time);}qInfo() << "Job done!";
}
在我们的主线程中,直接将Runable的子类对象传递给我们的QThreadPool,我们的QThreadPool回直接调用内部的run函数执行:
#include "Counter.h"
#include <QCoreApplication>
#include <QThreadPool>
int main(int argc, char* argv[]) {QCoreApplication a(argc, argv);QThread::currentThread()->setObjectName("Main");
QThreadPool* pool = QThreadPool::globalInstance();qInfo() << pool->maxThreadCount();
for (int i = 0; i < 100; i++) {Counter* counter = new Counter();counter->setAutoDelete(true);pool->start(counter);}
return a.exec();
}
现在就可以了,可以自己拷贝看看效果。
互斥机制:QMutex, QMutexLocker, QSemaphore, QWaitCondition
oh天,简直就跟报菜名一样,这四个东西都是用来保证互斥机制的。分别对应了我们的std::mutex. std::mutex_guard, std::counting_semaphore和std::condition_variable四个东西。
关于锁等并发编程,这里笔者不打算重复说明了。笔者有一个专门讲述高级并发编程的模块,感兴趣的朋友可以看看:
高阶开发基础——目录部分-CSDN博客
一个简单的demo
#include <QCoreApplication>
#include <QDebug>
#include <QList>
#include <QMutex>
#include <QMutexLocker>
#include <QSemaphore>
#include <QThread>
#include <QWaitCondition>
const int BufferSize = 5; // 缓冲区大小
QList<int> buffer; // 共享缓冲区
QMutex mutex; // 用于保护共享缓冲区
QSemaphore freeSpace(BufferSize); // 空闲空间信号量
QSemaphore usedSpace(0); // 已使用空间信号量
QWaitCondition bufferNotEmpty; // 缓冲区非空条件
QWaitCondition bufferNotFull; // 缓冲区未满条件
// 生产者线程
class Producer : public QThread {
protected:void run() override {for (int i = 0; i < 10; ++i) {freeSpace.acquire(); // 等待空闲空间mutex.lock(); // 锁定互斥量
// 如果缓冲区已满,等待缓冲区未满条件if (buffer.size() == BufferSize) {bufferNotFull.wait(&mutex);}
buffer.append(i); // 生产数据qDebug() << "Produced:" << i;
mutex.unlock(); // 解锁互斥量usedSpace.release(); // 增加已使用空间
// 通知消费者缓冲区非空bufferNotEmpty.wakeAll();}}
};
// 消费者线程
class Consumer : public QThread {
protected:void run() override {for (int i = 0; i < 10; ++i) {usedSpace.acquire(); // 等待已使用空间mutex.lock(); // 锁定互斥量
// 如果缓冲区为空,等待缓冲区非空条件if (buffer.isEmpty()) {bufferNotEmpty.wait(&mutex);}
int value = buffer.takeFirst(); // 消费数据qDebug() << "Consumed:" << value;
mutex.unlock(); // 解锁互斥量freeSpace.release(); // 增加空闲空间
// 通知生产者缓冲区未满bufferNotFull.wakeAll();}}
};
int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);
Producer producer;Consumer consumer;
producer.start(); // 启动生产者线程consumer.start(); // 启动消费者线程
producer.wait(); // 等待生产者线程结束consumer.wait(); // 等待消费者线程结束
return a.exec();
}
跨线程的通信
所以Qt如何完成跨线程的通信,即跨线程的信号与槽呢?答案是在使用QueueConnection,当然AutoConnection也可以,Qt6中会默认判断两个对象是否在同一个线程,不再的话就会自动选择QueueConnection。这个是将我们的信号与槽机制放置到了事件的监听循环队列当中去了。
#include <QCoreApplication>
#include <QDebug>
#include <QObject>
#include <QThread>
// 工作线程类
class Worker : public QObject {Q_OBJECT
public:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}
public slots:void doWork() {qDebug() << "Worker: Running in thread" << QThread::currentThreadId();
// 模拟耗时操作QThread::sleep(2);
// 发送完成信号emit workDone();}
signals:void workDone(); // 工作完成信号
};
// 主线程类
class Controller : public QObject {Q_OBJECT
public:explicit Controller(QObject *parent = nullptr) : QObject(parent) {}
void start() {qDebug() << "Controller: Running in thread"<< QThread::currentThreadId();
// 创建工作线程QThread *workerThread = new QThread;Worker *worker = new Worker;
// 将 Worker 移动到工作线程worker->moveToThread(workerThread);
// 连接信号与槽connect(workerThread, &QThread::started, worker, &Worker::doWork);connect(worker, &Worker::workDone, this, &Controller::handleWorkDone);connect(worker, &Worker::workDone, workerThread, &QThread::quit);connect(workerThread, &QThread::finished, worker, &Worker::deleteLater);connect(workerThread, &QThread::finished, workerThread,&QThread::deleteLater);
// 启动工作线程workerThread->start();}
public slots:void handleWorkDone() {qDebug() << "Controller: Work done, running in thread"<< QThread::currentThreadId();}
};
int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);
Controller controller;controller.start(); // 启动控制器
return a.exec();
}
#include "main.moc"
-
-
这里
#include "main.moc"是一个小的偷懒技巧,QT的元对象是依赖于元对象处理器的,我们include进来qt处理后的结果才好方便解决未定义的符号问题
-
-
2. Worker::workDone信号是从工作线程发射的,而Controller::handleWorkDone槽是在主线程中执行的。 -
-
由于信号与槽的连接类型是
Qt::QueuedConnection(默认跨线程连接类型),信号会被放入主线程的事件循环中,由主线程异步执行槽函数。
-
入门QtConcurrent,Qt集成的一个并发框架
为什么会有QtConcorrent呢?因为大部分情况下,我们可以不需要太客制化的并发(特殊场景除外)。我们总是希望将任务并行化但是不想要关心其具体的细节。
QtConcurrent就将并发进一步的提升为了一个经典的异步框架。我们只需要让一个任务并发的执行出去,而不需要关心它如何执行,在另一个时间点上我调用一个wait机制就把东西取回来(如果已经做完了直接返回,没有做完那就阻塞的等待直到做完了为之)
| 返回值 | 函数签名 |
|---|---|
QFuture<T> | run(Function function, ...) |
QFuture<T> | run(QThreadPool *pool, Function function, ...) |
这里,我们的Run就是直接的派发一个任务出去。返回回来的QFuture就是我们希望得到的一个结果。比如说,我们depatch了一个网络请求。需要其结果的时候,我们的结果就应该被存放在我们的QFuture里面。
直到我们获取的时候,我们才会获取我们想要的东西。我们调用的是waitForFinish这个函数,明确的表达我们需要结果后才会执行。
简单的说:QFuture 允许线程与一个或多个将在稍后某个时间点准备就绪的结果同步。结果可以是任何具有默认、复制和可能移动构造函数的类型。如果在调用 result()、resultAt()、results() 和 takeResult() 函数时结果不可用,QFuture 将等待,直到结果可用。您可以使用 isResultReadyAt() 函数来确定结果是否已准备就绪。对于报告多个结果的 QFuture 对象,resultCount() 函数返回连续结果的数量。这意味着从 0 到 resultCount() 迭代结果始终是安全的。takeResult() 会使未来无效,并且任何后续尝试访问未来结果的尝试都会导致未定义的行为。isValid() 告诉您是否可以访问结果。
QFuture 提供了 Java 样式的迭代器 (QFutureIterator) 和 STL 样式的迭代器 (QFuture::const_iterator)。使用这些迭代器是访问未来结果的另一种方式。
如果需要将一个异步计算的结果传递给另一个异步计算,QFuture 提供了一种使用 then() 链接多个顺序计算的便捷方法。onCanceled() 可用于添加在 QFuture 被取消时要调用的处理程序。此外,onFailed() 可用于处理链中发生的任何故障。请注意,QFuture 依赖于异常来进行错误处理。如果无法使用异常,您仍然可以通过将错误类型作为 QFuture 类型的一部分来指示 QFuture 的错误状态。例如,您可以使用 std::variant、std::any 或类似类型来保存结果或失败,或者创建自定义类型。
上面的部分是翻译Qt文档的结果。
如果需要使用到信号与槽的机制,我们还会需要使用的是QFutureWatcher来监控我们的QFuture的状态,这样,信号与槽机制就可以跟异步框架精密协调的工作起来了。
QFutureWatcher 中还提供了一些 QFuture 函数:progressValue()、progressMinimum()、progressMaximum()、progressText()、isStarted()、isFinished()、isRunning()、isCanceled()、isSuspending()、isSuspended()、waitForFinished()、result() 和 resultAt()。cancel()、setSuspended()、suspend()、resume() 和 toggleSuspended() 函数是 QFutureWatcher 中的插槽。 状态更改通过 started()、finished()、canceled()、suspending()、suspended()、resumed()、resultReadyAt() 和 resultsReadyAt() 信号报告。进度信息由 progressRangeChanged()、void progressValueChanged() 和 progressTextChanged() 信号提供。 节流控制由 setPendingResultsLimit() 函数提供。当待处理的 resultReadyAt() 或 resultsReadyAt() 信号的数量超过限制时,未来所代表的计算将自动受到限制。一旦待处理信号的数量低于限制,计算将恢复。
以及,Qt自身也提供了一个类似域LockGuard的机制,这里,我们的类名称是QFutureSynchronizer,可以看看这里的Qt文档。
一些参考
-
Qt 6 Core Advanced with C++ | Udemy
-
Qt Documentations Qt Documentation | All Documentation
相关文章:
系统讨论Qt的并发编程2——介绍一下Qt并发的一些常用的东西
目录 QThreadPool与QRunnable 互斥机制:QMutex, QMutexLocker, QSemaphore, QWaitCondition 跨线程的通信 入门QtConcurrent,Qt集成的一个并发框架 一些参考 QThreadPool与QRunnable QThreadPool自身预备了一些QThread。这样,我们就不需…...
【数据挖掘】Pandas之DataFrame
在 Pandas 中,DataFrame 提供了丰富的数据操作功能,包括 查询、编辑、分类和汇总。 1. 数据查询(Filtering & Querying) 1.1 按索引或列名查询 import pandas as pddata {"ID": [101, 102, 103, 104, 105],"…...
C++:volatile、const、mutable关键字
文章目录 volatile、const、mutable 关键字的作用、联系与区别 1️⃣ **volatile** —— 防止编译器优化,确保变量每次访问都从内存读取**作用****使用场景****示例** 2️⃣ **const** —— 限制变量的修改,保证不可变性**作用****使用场景****示例** 3️…...
linux离线安装miniconda环境
1 下载安装包 可以在官网下载最新版 https://www.anaconda.com/download/success#miniconda 或者在软件目录选择合适的版本 https://repo.anaconda.com/miniconda/ 安装包传入离线服务器 ./Miniconda3-py311_24.9.2-0-Linux-x86_64.sh2 运行安装包 ./Miniconda3-py311_24…...
考研408数据结构线性表核心知识点与易错点详解(附真题示例与避坑指南)
一、线性表基础概念 1.1 定义与分类 定义:线性表是由n(n≥0)个相同类型数据元素构成的有限序列,元素间呈线性关系。 分类: 顺序表:元素按逻辑顺序存储在一段连续的物理空间中(数组实现&…...
selenium用例执行过程采集操作形成测试报告上的回复
在代码执行的过程中不断的进行截图,把截图拼接成gif动态图,放在测试报告上 1、每条用例执行启动一个线程,这个线程会每隔0.3秒进行截图 项目下创建一个临时目录video用来存储所有截图以及gif动态图封装不断截图的方法,每隔0.3秒…...
多元数据直观表示(R语言)
一、实验目的: 通过上机试验,掌握R语言实施数据预处理及简单统计分析中的一些基本运算技巧与分析方法,进一步加深对R语言简单统计分析与图形展示的理解。 数据: 链接: https://pan.baidu.com/s/1kMdUWXuGCfZC06lklO5iXA 提取码: …...
【JavaEE】线程安全
【JavaEE】线程安全 一、引出线程安全二、引发线程安全的原因三、解决线程安全问题3.1 synchronized关键字(解决修改操作不是原子的)3.1.1 synchronized的特性3.1.1 synchronized的使用事例 3.2 volatile 关键字(解决内存可见性) …...
HarmonyOS 5.0应用开发——多线程Worker和@Sendable的使用方法
【高心星出品】 文章目录 多线程Worker和Sendable的使用方法开发步骤运行结果 多线程Worker和Sendable的使用方法 Worker在HarmonyOS中提供了一种多线程的实现方式,它允许开发者在后台线程中执行长耗时任务,从而避免阻塞主线程并提高应用的响应性。 S…...
华为OD-2024年E卷-分批萨[100分]
文章目录 题目描述输入描述输出描述用例1解题思路Python3源码 题目描述 吃货"和"馋嘴"两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。但是粗心的服务员将披萨切成了每块大小都完全不…...
SSH监控
创建/etc/ssh/sshrc文件 写入以命令 echo " 系统状态 " uptime free -h 每次登录会显示 如果在sshrc文件加入以下脚本每次登录就是执行这个脚本 # cat /etc/ssh/sshrc echo " 系统状态 " uptime free -h /usr/local/bin/monit.sh以…...
leetcode日记(74)扰乱字符串
很有难度的一题,一开始真的绕了很多思维上的弯路。 最开始的想法是递归,看到题目的时候想到动态规划但是完全没有思路应该怎么用,结果确实是递归动态规划。 最开始的想法是构建树,每一层包含这一步划分的方法(实际会…...
RV1126的OSD模块和SDL_TTF结合输出H264文件
目录 一.RV1126多线程处理输出OSD字符叠加图层的流程 1.1. VI模块的初始化 1.2. 初始化VENC模块: 1.3. 初始化RGN模块: 1.4. 绑定VI模块和VENC模块,伪代码如下 1.5. 创建多线程进行OSD字库的叠加: 1.6. 获取每一帧处理过后的…...
GEE:计算长时间序列NPP与NDVI之间的相关系数
GEE中内置了计算相关系数的函数,可以分析两个变量之间的相关性,比如要分析两个波段之间的相关性,主要用到ee.Reducer.pearsonsCorrelation()函数。 ee.Reducer.pearsonsCorrelation() 内容:创建一个双输入归约器,用于…...
水仙花数(华为OD)
题目描述 所谓水仙花数,是指一个n位的正整数,其各位数字的n次方和等于该数本身。 例如153是水仙花数,153是一个3位数,并且153 13 53 33。 输入描述 第一行输入一个整数n,表示一个n位的正整数。n在3到7之间&#x…...
【对话状态跟踪】关心整个对话过程用户完整意图变化
对话状态管理器 核心逻辑是解决键冲突和验证范围有效性, 但需依赖外部输入的正确性。在实际应用中, 可能需要结合用户提示或自动修正逻辑以提高鲁棒性。 NLU 槽 值 对儿 NLU的目的是把自然语言解析成结构化语义。结构化语义有多种表示方式,…...
【分享】网间数据摆渡系统,如何打破传输瓶颈,实现安全流转?
在数字化浪潮中,企业对数据安全愈发重视,网络隔离成为保护核心数据的重要手段。内外网隔离、办公网与研发网隔离等措施,虽为数据筑牢了防线,却也给数据传输带来了诸多难题。传统的数据传输方式在安全性、效率、管理等方面暴露出明…...
TikTok创作者市场关闭!全新平台TikTok One将带来哪些改变?
TikTok创作者市场关闭,全新平台TikTok One上线,创作者和品牌将迎来哪些新机遇? 近日,TikTok宣布关闭其原有的创作者市场(TikTok Creator Marketplace),并推出全新平台TikTok One。这一消息在社…...
LeetCode hot 100—矩阵置零
题目 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0,1],[0,0,0],[1,0,1]]示例 2࿱…...
部署Windows Server自带“工作文件夹”实现企业网盘功能完整步骤
前文已经讲解过Windows Server自带的“工作文件夹”功能,现以Windows Server 2025为例介绍部署工作文件夹的完整步骤: 为了确保您能够顺利部署和充分利用工作文件夹的功能,我将按照以下步骤进行讲解。 请注意,在域环境中部署工作…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
