Qt5.14.2 深入理解Qt多线程编程,掌握线程池架构实现高效并发

news2024/9/22 17:33:41

在高并发的软件系统中,多线程编程是解决性能瓶颈和提高系统吞吐量的有效手段。作为跨平台的应用程序开发框架,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多线程编程的实践技巧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1527289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Pytorch常用的函数(七)空洞卷积详解

Pytorch常用的函数(七)空洞卷积详解 1 初识空洞卷积 1.1 空洞卷积与语义分割任务 语义分割面临的困境&#xff1a; 与检测模型类似&#xff0c;语义分割模型也是建立是分类模型基础上的&#xff0c;即利用CNN网络来提取特征进行分类。在CNN分类模型中&#xff0c;一般情况下…

鸿蒙开发 一 (一)、框架了解

一、UI框架 开发范式名称 语言生态 UI更新方式 适用场景 适用人群 声明式开发范式 ArkTS语言 数据驱动更新 复杂度较大、团队合作度较高的程序 移动系统应用开发人员、系统应用开发人员 类Web开发范式 JS语言 数据驱动更新 界面较为简单的程序应用和卡片 Web前端…

2025张宇考研数学基础36讲,视频百度网盘+PDF

一、张宇老师全年高数体系&#xff08;听课用书指南&#xff09; 25张宇全程&#xff1a; docs.qq.com/doc/DTmtOa0Fzc0V3WElI 复制粘贴在浏览器上打开&#xff0c;就可以看到2025张宇的全部的啦&#xff01; 一般来说我们把考研数学划分为3-4个阶段&#xff0c;分别是基础阶…

详解基于快速排序算法的qsort的模拟实现

目录 1. 快速排序 1.1 快速排序理论分析 1.2 快速排序的模拟实现 2. qsort的模拟实现 2.1 qsort的理论分析 2.2 qsort的模拟实现 qsort函数是基于快速排序思想设计的可以针对任意数据类型的c语言函数。要对qsort进行模拟实现&#xff0c;首先就要理解快速排序。 1. 快…

【物联网应用】基于云计算的智能化温室种植一体化平台

目录 第一章 作品概述 1.1. 作品名称 1.2. 应用领域 1.3.主要功能 1.4.创新性说明 第二章 需求分析 2.1 现实背景 2.2 用户群体及系统功能 2.3 竞品分析 第三章 技术方案 3.1. 硬件组成与来源 3.2. 硬件设计合理性 3.3. 硬件系统设计图 3.4. 接口的通用性与可扩展性 3.5. 代码规…

【已解决】在pycharm终端无法激活conda环境,但在cmd命令行中可以

一、问题描述 在windows下winr启动cmd命令行&#xff0c;可以成功运行conda命令并且激活环境在pycharm中打开Terminal终端&#xff0c;发现无法运行conda和pip命令&#xff0c;报错环境无法激活 无法在管道中间运行文档: D:\software\Anaconda3\condabin\conda.bat。 所在位置…

uniapp+vue3+setup语法糖开发微信小程序时不能定义globalData的解决方法

在使用 uniapp 开发小程序的时候&#xff0c; 发现使用了setup 语法糖 &#xff0c;定义 globalData 时&#xff0c;要不是定义不了&#xff0c; 要不就是使用 getApp()取不到&#xff0c;后来想到一个不伦不类的方法解决了&#xff0c; 这个方法有点难看&#xff0c; 但是解决…

云手机的数据安全有保障吗?

随着移动互联网的迅速发展&#xff0c;云手机作为一种新兴的移动终端技术&#xff0c;正在逐渐受到人们的关注和应用。然而&#xff0c;对于云手机而言&#xff0c;数据安全问题一直是人们关注的焦点之一。本文将探讨云手机的数据安全性&#xff0c;并分析其是否具备足够的保障…

每周编辑精选|微软开源 Orca-Math 高质量数学数据集、清华大学研究团队发布条件去噪扩散模型 SPDiff...

Orca-Math 是微软研究院发布的数学推理模型&#xff0c;该模型展示了较小的专业模型在特定领域的价值&#xff0c;它们可以匹配甚至超越更大模型的性能。微软近期开源了用于训练 Orca-Math 的 Orca-Math-200K 数学单词问题数据集&#xff0c;现已在 hyper.ai 官网提供下载&…

记录对NSIS的一些微调 实现Electron安装包美化

利洽科技-nsNiuniuSkinUI - NSIS 实现了electron 的安装包美化&#xff0c;免费&#xff0c;便捷。 下面我整理了一些关于它的微调&#xff0c;使其安装卸载更加简单快捷。 1. 默认展示安装路径部分 &#xff08;1&#xff09;将moreconfiginfo标签visible 设置为 true&#…

const,static深度总结——c++穿透式分析

前言&#xff1b;c类和对象的知识点中除了几种默认函数&#xff0c; 比较重要的还有使用const和static修饰成员相关知识点。const在c中特性很简单。 但是在使用中&#xff0c; 比较容易疏忽大意出现问题。 static特性也很简单&#xff0c; 但是比起const来要直接的多。 在使用中…

PHP-小皮创建php网站中遇到的问题及解决方案—我耀学IT

一、安装 1.1 在学习php时我们需要用到的软件有两个&#xff0c;一个时vscode&#xff0c;一个就是小皮面板&#xff08;phpstudy&#xff09; 1.2 vscode安装直接从官网下载&#xff0c;根据系统下载对应的版本&#xff0c;例如&#xff1a;windows64、linux等&#xff1b;同…

智慧城市中的智慧生活:便捷、舒适与高效

目录 一、智慧城市中的智慧生活概述 二、智慧生活带来的便捷性 1、智慧交通的便捷出行 2、智慧购物的轻松体验 3、智慧政务的一站式服务 三、智慧生活带来的舒适性 1、智慧环境的绿色宜居 2、智慧医疗的健康保障 3、智慧教育的均衡发展 四、智慧生活带来的高效性 1、…

力扣每日一题 区域和检索 - 数组不可变 前缀和

Problem: 303. 区域和检索 - 数组不可变 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( n ) O(n) O(n) Code class NumArray {int[] s;public NumArray(int[] nums) {int n nums.length;s new int[n 1];for(int i 1; i < n; i)s[i] s[i-1] nums[i-1];}public …

蓝桥杯练习01卡片化标签

卡片化标签页 介绍 选项卡功能在前端开发中特别常见&#xff0c;作为设置选项的模块&#xff0c;每个选项卡代表一个活动的区域&#xff0c;点击不同的区域&#xff0c;即可展现不同的内容&#xff0c;这样既能节约页面的空间又能提升页面性能。 本题需要在已提供的基础项目中…

Vue/Uni-app/微信小程序 v-if 设置出场/退出动画(页面交互不死板,看起来更流畅)

天梦星服务平台 (tmxkj.top)https://tmxkj.top/#/ 在Vue.js中&#xff0c;使用v-if进行条件渲染时设置动画可以通过<transition>组件来实现。 具体操作步骤如下&#xff1a; 包裹条件渲染的元素&#xff1a;您需要将要通过v-if控制显示隐藏的元素包裹在<transition…

华为配置终端定位基本实验配置

配置终端定位基本示例 组网图形 图1 配置终端定位基本服务示例 组网需求数据准备配置思路配置注意事项操作步骤配置文件 组网需求 如图1所示&#xff0c;某公司网络中&#xff0c;中心AP直接与RU连接。 管理员希望通过RU收集Wi-Fi终端信息&#xff0c;并提供给定位服务器进行定…

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之二 素描画风格效果

Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之二 素描画风格效果 目录 Python 基于 OpenCV 视觉图像处理实战 之 OpenCV 简单实战案例 之二 素描画风格效果 一、简单介绍 二、素描画风格效果实现原理 三、案例简单实现步骤 一、简单介绍 Python是一种跨…

机器人可反向驱动能力与力控架构

反向驱动性是电机传动系统的机械特性&#xff0c;它描述了运动是否可以轻松反转 。特别是&#xff0c;反向驱动能力取决于两个因素&#xff1a;传动运动效率和整体执行器机械阻抗。反向运动中传动装置的低运动效率意味着所施加的外力的大部分被运动反作用力抵消。然而&#xff…

T100中常用的SQL语句

汇总总结一下在T100中常用的SQL语句。 一、基础资料的查询 这里把aimm200中的所有的需要的数据全部串查到一个结果集中&#xff0c;然后直接Excel汇出即可。 select imafsite 据点,imaf001 料件编号,imaal003 品名,imaal004 规格,--imaf172 交货前置时间,imaf013 补给策略imaa…