论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
只需一步,快速开始
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
IT评测·应用市场-qidao123.com
»
论坛
›
软件与程序人生
›
云原生
›
Qt5.14.2 深入理解Qt多线程编程,掌握线程池架构实现高 ...
Qt5.14.2 深入理解Qt多线程编程,掌握线程池架构实现高效并发 ...
惊落一身雪
金牌会员
|
2024-6-11 19:15:24
|
显示全部楼层
|
阅读模式
楼主
主题
998
|
帖子
998
|
积分
2994
在高并发的软件体系中,多线程编程是解决性能瓶颈和提高体系吞吐量的有效手段。作为跨平台的应用程序开发框架,Qt为我们提供了强大的多线程支持。本文将深入探究Qt多线程编程的实现细节,并先容线程池的计划思想,资助读者彻底掌握Qt多线程编程本领。
一、Qt的两种多线程实现方式分析
Qt中实现多线程编程主要有两种方式:重写QThread类的run()函数和使用信号与槽。
1、重写QThread的run()函数
这种方式需要继续QThread类并重写虚函数run(),将需要并发执行的代码逻辑放在run()函数中。例如:
class WorkThread : public QThread {
public:
void run() override {
//并发执行的代码
qDebug() << "Current thread:" << QThread::currentThreadId();
//执行耗时操作
heavyWorkLoad();
}
};
复制代码
在主线程中,我们只需创建WorkThread对象并调用start()即可启动新线程:
WorkThread *worker = new WorkThread;
worker->start();
复制代码
这种方法的长处是直观简单,缺点是run()函数作为线程执行体只能有一个入口,不太得当处置惩罚多个工作单元并发执行的场景。
2、使用信号与槽方式
Qt的信号与槽机制也可以用于实现多线程编程,它的思绪是:
(1)、创建QThread对象作为新线程
(2)、创建执行体对象,并使用QObject::moveToThread()将其移动到新线程
(3)、在主线程通过连接信号与槽的方式,间接调用执行体对象的槽函数,从而启动新线程中的使命
具体代码如下:
//ExecutionBody.h
class ExecutionBody : public QObject {
Q_OBJECT
public slots:
void execution() {
//并发执行的代码
qDebug() << "Executing in thread" << QThread::currentThreadId();
heavyWorkLoad();
}
};
//main.cpp
int main() {
QThread *worker = new QThread;
ExecutionBody *body = new ExecutionBody;
body->moveToThread(worker);
QObject::connect(worker, &QThread::started, body, &ExecutionBody::execution);
worker->start();
return app.exec();
}
复制代码
相比第一种方法,信号与槽方式支持在新线程中执行多个函数,更加灵活。但也相对复杂一些,开发者需要清晰地理解信号连接、变乱循环等概念。
二、突破瓶颈,构建高效线程池
前面先容了Qt的基本多线程实现方式,不外在实际项目中,如果只是简单地启动固定数量的线程,可能会面临以下问题:
(1)、线程的创建和销毁代价较高
(2)、线程数量太多,会加重体系的线程调度开销
(3)、大量线程空转,造成CPU资源浪费
为了解决这些问题,我们需要引入线程池的概念,将闲置的线程资源统一管理和调度,避免频繁创建和销毁线程。Qt提供了QThreadPool类实现了这一机制。
1、QThreadPool计划原理
QThreadPool内部管理了一组工作线程(工作者线程),当有使命投递时,线程池会将使命分配给空闲的工作线程执行,避免频繁创建和销毁线程。别的,QThreadPool还支持设置活跃线程数上限,在线程全部忙碌时也不会盲目创建新的工作线程,从而避免过分占用体系资源。
QThreadPool采用信号与槽的方式将使命分发给工作线程。具体来说,当我们调用QThreadPool::start()投递使命时,QThreadPool会为使命创建一个QRunnable对象,并通过内部信号连接到某个工作线程,由工作线程执行QRunnable的run()函数。
2、QThreadPool使用示例
下面通过一个简单的例子展示怎样使用QThreadPool:
//WorkerTask.h
class WorkerTask : public QRunnable {
public:
void run() override {
//执行任务逻辑
qDebug() << "Executing task in thread" << QThread::currentThreadId();
heavyWorkLoad();
}
};
//main.cpp
int main() {
QThreadPool *pool = QThreadPool::globalInstance();
//设置最大线程数
pool->setMaxThreadCount(QThread::idealThreadCount());
//投递任务
for(int i=0; i<200; ++i) {
WorkerTask *task = new WorkerTask;
pool->start(task);
}
return app.exec();
}
复制代码
这个示例起首获取全局QThreadPool实例,并设置最大工作线程数为当前体系的理想线程数(通常为CPU核心数)。然后循环构建WorkerTask对象并调用QThreadPool::start()投递,线程池会主动将使命分发给空闲的线程执行。
需要注意的是,QThreadPool默认采用栈内存管理QRunnable对象,也就是说在QRunnable的run()函数执行完毕后,QThreadPool会主动销毁对象。如果我们需要在run()函数执行完毕后继续访问QRunnable对象的数据成员,应该设置QThreadPool的stackSize属性(即将对象放在堆内存分配)。
三、多线程开发中的注意事项
尽管QThreadPool大大简化了多线程编程流程,但在实际开发中,我们仍需注意一些埋伏的安全隐患和性能风险:
1、线程间数据访问安全
当多个线程并发访问同一份数据时,很轻易出现竞态条件。Qt提供了QMutex、QSemaphore、QReadWriteLock等同步原语类,我们可以使用它们来保护线程间共享数据的完整性。
别的,Qt还提供了QAtomicInteger和QAtomicPointer等原子操纵类,可以大概确保底子数据类型的读写操纵的原子性。对于简单的计数、状态位的读写,使用原子操纵类可以避免加锁开销。
2、使命队列控制战略
使用QThreadPool虽然能避免频繁创建销毁线程,但如果使命投递过多且执行时间过长,使命队列会一连积压,可能导致相应延迟或内存占用過高。
因此,我们需要对使命队列的长度作出合理控制。QThreadPool提供了两个相干的API:
QThreadPool::reserveThread()可以为高优先级使命预留线程资源
QThreadPool::setMaxThreadCount()可以动态调整线程池的最大线程数
我们可以在投递使命前检查当前队列长度,对于优先级较高的使命使用reserveThread()保留资源,对于优先级较低的使命可以选择延迟投递或动态增加线程池大小。
3、避免死锁
在多线程编程中,如果多个线程互相持有对方所需要的锁资源,就会发存亡锁。例如下面的代码:
QMutex mutex1, mutex2;
//线程1
mutex1.lock();
...
mutex2.lock(); //阻塞
//线程2
mutex2.lock();
...
mutex1.lock(); //阻塞
复制代码
避免死锁的一个常用战略是:对全部需要加锁的代码采用统一的加锁顺序,每个线程按相同顺序申请锁。
4、镌汰线程切换开销
线程切换是一个非常耗时的操纵,会带来较大的性能开销。我们应该只管镌汰线程切换的发生,例如:
将密集计算的代码块集中在一个或几个线程中,避免在多个线程间切换
避免线程中的循环中壅闭操纵(如休眠、加锁等),这会使该线程长时间占用CPU
采用无锁编程,使用原子操纵和内存屏障指令实现线程安全操纵
通过本文的先容,希望你可以大概加深对Qt多线程编程的理解,在实际开发中合理使用多线程,提高应用程序的整体性能。下一篇文章将为你带来更多实战案例,进一步展示Qt多线程编程的实践本领。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
惊落一身雪
金牌会员
这个人很懒什么都没写!
楼主热帖
看完这个,还不会DVMA,请你吃瓜 ...
腾讯叶聪:朋友圈爆款背后的计算机视觉 ...
图文结合带你搞懂InnoDB MVCC
数据湖选型指南|Hudi vs Iceberg 数据 ...
rust中的超时处理
一个故事看懂CPU的SIMD技术
处理接口幂等性的两种常见方案 ...
聊一聊 TLS/SSL
Kubernetes(k8s)CNI(flannel)网络 ...
人工智能对软件测试的影响
标签云
AI
运维
CIO
存储
服务器
快速回复
返回顶部
返回列表