Qt中的多线程
Qt中有多种方法实现多线程:
- QThread
- QThreadPool和QPunnable(重用线程)
- Qt Concurrent
- WorkerScript(QML中的线程)
QThread 在上两篇文章中已经解释了,这里就不再赘述。
QThreadPoo和QRunnable(实现的多线程)
QRunnable:
QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由 run 函数的重新实现表示。
| autoDelete() | 返回是否自动删除 |
| setAutoDelete() | 设置自动删除(thue) |
设置自动删除的话,在QThreadPool中会自动删除QRunnable
使用的注意事项:
启用自动删除时,使用相同的 QRunnable 多次调用 QThreadPoool::start() 会产生争用条件,不建议这样做。
QRunnable和QThread的区别:
QThreadPool(线程池)
顾名思义,是有很多线程的池子, 可以管理池子中的线程。

线程池的优点:
- 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控。
常用函数:
| setMaxThreadCount() | 设置线程池中最大的线程数,默认:QThread::idealThreadCount |
| setExpiryTimeout(int) | 将超时xx毫秒未使用的线程视为已过期并将退出,已在运行的线程没有影响。建议在创建线程池后立即设置。 |
| setStackSize() | 设置线程池工作线程的堆栈大小,当线程池创建新线程时使用, 默认值为 0 |
| clear() | 清除队列中未开启的可运行项 |
| globalInstance() | 返回一个全局QThreadPool实例 |
| reserveThread() | 保留一个线程( 此函数将始终增加活动线程数) |
| releaseThread() | 释放reserveThread()保留的线程 |
| start(QRunnable,int) | 开启一个线程,可以设置优先级 |
| tryStart() | 尝试开启一个线程 |
| tryTake() | 尝试从队列中删除指定的尚未启动的可运行对象(谨慎使用) |
| waitForDone(int) | 等待所有线程退出最多毫秒毫秒,并从线程池中删除所有线程。如果删除了所有线程返回true |
| activeThreadCount() | 当前活跃的线程数 |
启动QRunnable线程的方法:
- 使用QThreadPool::globalInstance()->start()启动 (全局)
- 创建一个QThreadPool 对象,然后通过该对象启动
Runnable类:
#ifndef RUNNABLE_H
#define RUNNABLE_H
#include<QThread>
#include<QDebug>
#include<QRunnable>class Runnable:public QRunnable
{
public:explicit Runnable()//构造函数{}~Runnable()//析构函数{}void run()//重写run函数{qDebug()<<"Runnable中的run函数";qDebug()<<"thread to run():"<<QThread::currentThreadId();}
};
#endif // RUNNABLE_H
1.使用QThreadPool::globalInstance()->start()启动 (全局)
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);qDebug()<<"主线程号:"<<QThread::currentThread();Runnable *able=new Runnable;QThreadPool::globalInstance()->start(able);}
2.创建一个QThreadPool 对象,然后通过该对象启动
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);qDebug()<<"主线程号:"<<QThread::currentThread();Runnable *able=new Runnable;//QThreadPool::globalInstance()->start(able);QThreadPool pool;pool.start(able);}
线程池函数的使用
QThreadPool pool;qDebug()<<"本机的线程数"<<QThread::idealThreadCount();qDebug()<<"线程池中的线程数"<<pool.maxThreadCount();pool.setMaxThreadCount(10);//设置线程池中最大的线程数pool.setExpiryTimeout(40000);//如果线程40000毫秒,40秒未使用,使它过期Runnable *runnable=new Runnable;pool.reserveThread();//保留一个线程pool.start(runnable);//开启线程qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();pool.releaseThread();//释放那个保留的线程qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();
QRunnable释放内存问题:
QRunnable可以执行完释放内存:需要将autoDelete()设置为true,默认为true。
#ifndef RUNNABLE_H
#define RUNNABLE_H
#include<QThread>
#include<QDebug>
#include<QRunnable>class Runnable:public QRunnable
{
public:explicit Runnable()//构造函数{}~Runnable()//析构函数{qDebug()<<__FUNCTION__;}void run()//重写run函数{qDebug()<<"thread to run():"<<QThread::currentThreadId();}
};
#endif // RUNNABLE_H
QThreadPool pool;Runnable *runnable=new Runnable;pool.start(runnable);//开启线程
执行完后自动调用析构函数。
QRunnable的通信问题:
由于QRunnable是单独的一个类,并没有继承QObject,所以没有connect()函数,所以无法于外界进行通信。
解决该问题的方法:
- 使用QMetaObject::invokeMethod()
- 使用多继承的方式,同时继承QRunnable和QObject,但这样的话会使接口混乱(尽量不要使用)
QMetaObject::invokeMethod()的创建方法 :
QMetaObject::invokeMethod(QObject *obj, //使用的类const char *member, //调用函数名Qt::ConnectionType type,//调用类型QGenericReturnArgument ret,//用来存储返回值的类型QGenericArgument val0 = QGenericArgument(nullptr),QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(),QGenericArgument val4 = QGenericArgument(),QGenericArgument val5 = QGenericArgument(),QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(),QGenericArgument val9 = QGenericArgument())//最多有10个参数
*obj:需要把使用类传进去
member:函数的名称
type:调用类型:
- Qt::DirectConnnect 立即调用该成员函数
- Qt::QueuedConnection 一旦应用程序进入主事件循环,就会发送 QEvent 并调用成员。
- Qt::BlockQueuedConnection 当前线程将阻塞,直到事件被传递。使用此连接类型在同一线程中的对象之间进行通信将导致死锁。
- Qt::AutoConnection 如果 obj 与调用方位于同一线程中,则同步调用成员;否则,它将异步调用该成员。
val0-val9:最多可以有10个参数
传参的方式:使用宏传参
- Q_ARG(type,const Type&value)
- Q_RETURN_ARG(type, Type&value)
QString S="hellow";
QMetaObject::invokeMethod(obj, "Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));
Runnable函数:
#ifndef RUNNABLE_H
#define RUNNABLE_H
#include "widget.h"
#include<QThread>
#include<QObject>
#include<QDebug>
#include<QRunnable>
#include<QMetaObject>class Runnable:public QRunnable
{
public:explicit Runnable(QObject *obj1):obj(obj1)//构造函数{}~Runnable()//析构函数{}void run()//重写run函数{ QString S="hellow";QMetaObject::invokeMethod(obj,"Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));qDebug()<<"thread to run():"<<QThread::currentThreadId();}QObject *obj=nullptr;//需要传的对象
};
#endif // RUNNABLE_H
主函数:
主类中创建函数开头需要添加:Q_INVOKABLE(这样才直到调用哪一个函数)
#include "widget.h"
#include "ui_widget.h"
#include"runnable.h"
#include<QThreadPool>
class Rnnable;
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QThreadPool pool;Runnable *runnable=new Runnable(this);//传入调用的对象pool.start(runnable);//开启线程
}
Q_INVOKABLE void Widget::Text(QString a,QString b)//创建一个函数,名称要相同
{qDebug()<<a;qDebug()<<b;
}
Widget::~Widget()
{delete ui;
}
Qt Concurrent(并发)
QtConcurrent命名空间提供高级 API,可以在不使用互斥锁、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。
使用 QtConcurrent编写的程序会根据可用的处理器内核数量自动调整使用的线程数。这意味着编写的应用程序在部署到多核系统时将自动扩展。
如何使用Qt Concurrent:
1.pro文件中添加
QT +=concurrent
2. 添加头文件
#include<QtConcurrent>
以下是如何执行QtConcurrent的使用方法:
使用QtConcurrent::run()函数执行内容
函数模型:
//T为模板类型QFuture<T> QtConCurrent::run(Function)//执行一个函数QFuture<T> QtConCurrent::run(threadpool,Function)//线程池中,取出线程执行function函数
执行一个函数:
void text()
{qDebug()<<"ok";
}
void text1(int a)
{qDebug()<<a;
}
void text2(QString s1,QString s2)
{qDebug()<<s1+s2;
}
int main(int argc, char *argv[])
{QApplication a(argc, argv);QFuture<void> fu=QtConcurrent::run(text);QFuture<void> fu1=QtConcurrent::run(text1,10);QFuture<void> fu2=QtConcurrent::run(text2,QString("aa"),QString("bb")");fu.waitForFinished();//等待完成fu1.waitForFinished();//等待完成fu2.waitForFinished();//等待完成return a.exec();
}
通过线程池中的进程执行函数
void text1(int a)
{qDebug()<<a;
}int main(int argc, char *argv[])
{QApplication a(argc, argv);QThreadPool pool;QFuture<void> fu=QtConcurrent::run(&pool,text1,10);fu.waitForFinished();//等待完成return a.exec();
}
3.调用常量成员函数:
run()函数可以接收成员函数的指针,第一个参数必须是常量引用或指向类实例的指针。
QByteArray bytearray = "hello world";
QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
QList<QByteArray> result = future.result();
4.调用非常量成员函数:
QImage image = ...;
QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels,QImage::InvertRgba);
future.waitForFinished();
总结:以上三种多线程的特点:
| 特征 | QThread | QRunnable和QThreadPool | QtConcurrent::run() | QtConcurrent (Map,Filter,Reduce) |
| 指定优先级 | 是 | 是 | ||
| 可运行 事件循环 | 是 | |||
| 线程可以通过 信号接收数据更新 | 是 | |||
| 可以使用 信号控制螺纹 | 是 | 是(QFutureWatcher) | ||
| 可以通过 QFuture监控线程 | 部分是 | 是 | ||
| 内置暂停/ 恢复/取消功能 | 是 |
适用场景:
| 生命周期 | 开发任务 | 解决方案 |
| 持续运行 | 在另一个线程中重复执行昂贵的操作,其中线程不需要接收任何信号或事件。 | 直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号以将数据发送回 GUI 线程。 |
| 持续运行 | 让一个对象存在于另一个线程中,该线程可以根据请求执行不同的任务和/或可以接收要使用的新数据。 | 子类 一个QObject 以创建工作线程。实例化此工作线程对象和 QThread。将工作线程移动到新线程。通过排队的信号槽连接将命令或数据发送到工作器对象。 |
| 一次调用 | 在另一个线程中运行新的线性函数,可以选择在运行期间更新进度。 | Qt提供不同的解决方案:
|
| 一次调用 | 在另一个线程中运行现有函数并获取其返回值。 | 使用 QtConcurrent::run() 运行函数。让 QFutureWatcher 在函数返回时发出 done() 信号,并调用 QFutureWatcher::result() 来获取函数的返回值。 |
| 一次调用 | 使用所有可用核心对容器的所有项执行操作。例如,从图像列表生成缩略图。 | 使用 Qt Concurrent 的 QtConcurrent::filter() 函数选择容器元素,使用 QtConcurrent::map() 函数对每个元素应用操作。要将输出折叠成单个结果,请改用 QtConcurrent::filteredReduction() 和 QtConcurrent::mappedReduction()。 |
参考文章:
Thread Support in Qt | Qt 5.15
Qt线程之QRunnable的使用详解_luoyayun361的博客-CSDN博客_qrunnable
【QT】继承QRunnable+QThreadPool实现多线程_李春港的博客-CSDN博客_继承qrunnable
Qt并发模块Qt Concurrent的使用_Amnesia Greens的博客-CSDN博客_qtconcurrent
相关文章:
Qt中的多线程
Qt中有多种方法实现多线程: QThreadQThreadPool和QPunnable(重用线程)Qt ConcurrentWorkerScript(QML中的线程)QThread 在上两篇文章中已经解释了,这里就不再赘述。 QThreadPoo和QRunnable(实现…...
React-Hooks怎样封装防抖和节流-面试真题
Debounce debounce 原意消除抖动,对于事件触发频繁的场景,只有最后由程序控制的事件是有效的。 防抖函数,我们需要做的是在一件事触发的时候设置一个定时器使事件延迟发生,在定时器期间事件再次触发的话则清除重置定时器ÿ…...
算法训练营 day51 动态规划 打家劫舍系列
算法训练营 day51 动态规划 打家劫舍系列 打家劫舍 198. 打家劫舍 - 力扣(LeetCode) 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#…...
【蓝桥集训】第六天——递归
作者:指针不指南吗 专栏:Acwing 蓝桥集训每日一题 🐾或许会很慢,但是不可以停下来🐾 文章目录1.树的遍历2.递归求阶乘3.求斐波那契数列1.树的遍历 一个二叉树,树中每个节点的权值互不相同。 现在给出它的后…...
react源码中的hooks
今天,让我们一起深入探究 React Hook 的实现方法,以便更好的理解它。但是,它的各种神奇特性的不足是,一旦出现问题,调试非常困难,这是由于它的背后是由复杂的堆栈追踪(stack trace)支…...
038.Solidity入门——25调用其他合约的方法
Solidity 提供了几种方式用于调用其他合约:方法描述直接调用使用 address.call 函数,可以向另一个合约发送消息并返回结果。低级调用使用 address.call 或 address.callcode 函数,可以执行一个外部合约中的代码。与直接调用不同,低…...
Revit项目浏览器的标准设置应用和快速视图样板?
一、Revit项目浏览器的标准设置应用 设计院阶段的BIM应用,主要是Revit出施工图方面,需要涉及到很多标准的制定方面的问题,而且这个标准不仅仅是一个命名标准,还有很多的符合本院的出图标准等等,本期就不做详细讨论&…...
安装MQTT Server遇到报错“cannot verify mosquitto.org‘s certificate”,该如何解决?
MQTT是基于发布/订阅的轻量级即时通讯协议,很适合用于低带宽、不稳定的网络中进行远程传感器和控制设备通讯等操作中。在我们的软件研发中,也经常使用MQTT协议进行消息通信等。今天来和大家分享一些关于在安装MQTT Server中遇到的疑难问题及解决思路。当…...
程序员如何向架构师转型?看完就明白该怎么做了
软件行业技术开发从业人员众多,但具备若干年开发经验的普通的开发人员往往面临个人发展的瓶颈,即如何从普通开发人员转型成高层次的系统架构师和技术管理人员。想成为一名架构师,应当具备全面的知识体系,需要进行系统的学习和实践…...
Flask入门(9):蓝图
目录9.蓝图9.1 概述9.2 蓝图项目结构结构1结构29.3 添加前缀9.4 静态文件9.5 模板9.6 构建 URLs9.蓝图 参考:http://www.pythondoc.com/flask/blueprints.html 9.1 概述 Flask 使用了 蓝图 的概念在一个应用或者跨应用中构建应用组件以及支持通用模式。 蓝图很好…...
跑步戴哪种耳机好,最适合运动跑步的蓝牙耳机
经常跑步使用的耳机,还是要选择佩戴着舒适以及牢固的运动耳机最为合适,在运动当中会遇到耳机掉落或者长时间佩戴耳道感到难受的现象发生,那么什么蓝牙耳机是最适合运动当中佩戴呢?下面这些耳机分享希望能够帮助大家。 1、南卡Run…...
微信小程序实现瀑布流布局
微信小程序实现瀑布流布局1、简单实例,纯图片后台返回图片高度https://blog.csdn.net/qq_45967222/article/details/1190318762、纯图片后台返回图片高度、通过wx.getImageInfo获取在线图片高度、按照奇数偶数来显示https://blog.csdn.net/baidu_35290582/article/d…...
2023最新网络工程师HCIA-Datacom“1000”道题库,光速刷题拿证
HCIA认证是华为认证体系的初级认证,可以说是网工进入IT行业的一张从业资格证! HCIA-Datacom考试覆盖数通基础知识 包括 TCP/IP 协议栈基础知识,OSPF 路由协议基本原理以及在华为路由器中的配置实现,以太网技术、生成树、VLAN 原…...
[蓝桥杯] 递归与递推习题训练
文章目录 一、递归实现指数型枚举 1、1 题目描述 1、2 题解关键思路与解答 二、递归实现排列型枚举 2、1 题目描述 2、2 题解关键思路与解答 三、递归实现组合型枚举 3、1 题目描述 3、2 题解关键思路与解答 四、带分数 4、1 题目描述 4、2 题解关键思路与解答 五、费解的开关…...
领航智能汽车信息安全新征程 | 云驰未来乔迁新址
2月20日,在北京朝阳百子湾东朝时代创意园,云驰未来迎来乔迁之喜,智能汽车和自动驾驶领域的行业领导、合作伙伴与客户、投资人及媒体嘉宾齐聚现场,共同见证云驰未来迈上新的发展征程。 作为中国智能网联汽车和自动驾驶信息安全行业…...
Kaldi语音识别技术(七) ----- 训练GMM
Kaldi语音识别技术(七) ----- GMM 文章目录Kaldi语音识别技术(七) ----- GMM训练GMMtrain_mono.sh 用于训练GMM训练GMM—生成文件训练GMM—final模型查看训练GMM—final.occs查看训练GMM—对齐信息查看训练GMM—fsts.*.gz查看训练GMM—tree决策树查看align_si.sh 用于对齐训练G…...
Java 集合基础
文章目录一、集合概念二、ArrayList1. 构造方法和添加方法2. 常用方法三、案例演示1. 存储字符串并遍历2. 存储学生对象并遍历3. 键盘录入学生对象并遍历一、集合概念 编程的时候如果要存储多个数据,使用长度固定的数组存储格式,不一定满足我们的需要&a…...
Day896.MySql的kill命令 -MySQL实战
MySql的kill命令 Hi,我是阿昌,今天学习记录的是关于MySql的kill命令的内容。 在 MySQL 中有两个 kill 命令: 一个是 kill query 线程 id,表示终止这个线程中正在执行的语句;一个是 kill connection 线程 id&#…...
L2-010 排座位
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。 输入格式࿱…...
C++的完美讲解,还不快来看看?
目录 简介: 创建C程序: Windows编译简介: Hello,C World! 简介: C融合了3中不同的编程传统:C语言代表的过程性传统、C在C语言基础上添加的类代表的面向对象语言的传统以及C模板支持的通用编程传统。一般来说,计算机语言…...
学术写作“变形记”:书匠策AI如何让课程论文从“青铜”变“王者”——解锁AI时代论文写作新姿势
论文写作,曾是无数学生的“噩梦”:选题撞车、文献堆积如山、逻辑混乱如麻、格式调整让人抓狂……如今,随着人工智能技术的爆发,学术写作的“游戏规则”正在被彻底改写。书匠策AI(官网:www.shujiangce.com&a…...
Phi-4-mini-reasoning应对软件测试:自动生成测试用例与缺陷分析
Phi-4-mini-reasoning应对软件测试:自动生成测试用例与缺陷分析 1. 引言:软件测试的痛点与AI解决方案 在软件开发的生命周期中,测试环节往往占据30%-50%的项目时间。传统测试工作面临两大核心挑战:一是测试用例设计需要大量人工…...
实战应用:基于快马AI与OpenClaw构建Mac本地电商价格监控系统
最近在做一个电商价格监控的小工具,发现用OpenClaw配合Mac本地环境搭建特别方便。这里分享一下我的实战经验,希望能帮到有类似需求的同学。 为什么选择OpenClaw OpenClaw是个轻量级的Python爬虫框架,特别适合需要快速搭建数据采集系统的场景…...
AI专著写作快车道:特色工具大集合,助力科研成果出版
学术专著写作困境与AI工具助力 学术专著的写作并不只是简单的“写出来”,更在于能否顺利“出版、得到认可”。在当前的出版市场,学术专著的受众本就相对有限,因此出版社对学术价值和作者的影响力要求非常高。许多作者虽然完成了初稿…...
Godep历史意义揭秘:Go依赖管理工具的开创者如何改变开发方式
Godep历史意义揭秘:Go依赖管理工具的开创者如何改变开发方式 【免费下载链接】godep dependency tool for go 项目地址: https://gitcode.com/gh_mirrors/go/godep Godep作为Go语言依赖管理工具的开创者,在Go生态系统的演进历程中扮演了至关重要的…...
RouterOS网桥VLAN实战:从零构建安全隔离的二层虚拟网络
1. VLAN基础与RouterOS网桥概述 刚接触网络管理的朋友可能经常听到"VLAN"这个词,但总觉得它神秘莫测。其实VLAN就像给一栋办公楼划分不同部门:财务部、研发部、市场部各自有独立的办公区域,既保证了隐私安全,又避免了相…...
Qwen3.5-2B实战入门:20亿参数多模态模型图文对话快速上手指南
Qwen3.5-2B实战入门:20亿参数多模态模型图文对话快速上手指南 1. 认识Qwen3.5-2B Qwen3.5-2B是一款轻量级多模态基础模型,属于Qwen3.5系列的小参数版本(20亿参数)。这个模型特别适合在资源有限的设备上运行,比如个人…...
Blender 3MF插件终极指南:从零开始掌握3D打印文件格式
Blender 3MF插件终极指南:从零开始掌握3D打印文件格式 【免费下载链接】Blender3mfFormat Blender add-on to import/export 3MF files 项目地址: https://gitcode.com/gh_mirrors/bl/Blender3mfFormat 3MF(3D Manufacturing Format)格…...
7天掌握Driver Store Explorer:Windows驱动管理的完整指南
7天掌握Driver Store Explorer:Windows驱动管理的完整指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer [RAPR] 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer Driver Store Explorer(简称RAPR)是…...
交叉编译microcom
由于默认的busybox没有支持microcom工具,也没有提供源码,所以需要自己交叉编译microcom工具。 microcom工具 https://packages.ubuntu.com/zh-cn/plucky/microcom 下载ubuntu带的软件包microcom,下载microcom_2023.09.0.orig.tar.xz版本&…...
