QT-多线程、线程池的使用

news/2024/12/22 1:01:54/

在进行桌面应用程序开发的时候, 假设应用程序在某些情况下需要处理比较复杂的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验,还可以提升程序的执行效率。

qt 中使用了多线程,有些事项是需要额外注意的:

  • 默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新
  • 子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理
  • 主线程和子线程之间如果要进行数据的传递,则需要使用Qt中的信号槽机制

一、QT多线程实现方法(1)- 基于QThread

  • 创建subThread线程类,继承QThread类
  • 重写父类的run()方法,即具体要执行的任务
  • 主线程中创建子线程对象,调用start()方法启动子线程

二、QT多线程实现方法(2)- 基于QObject

  • 创建工作类,继承QObject类
  • 添加一个公共的成员函数,函数体就是子线程要执行的业务逻辑(后面把这个任务函数作为槽函数使用)
  • 主线程中创建子线程对象
  • 创建工作类对象(注意不能给他指定父对象)
  • 将工作类对象移动到创建的子线程的对象中,需要调用QObject的moveToThread()方法
  • 启动子线程,调用start()方法,但此时线程虽然启动了,但移动到线程中的对象没有工作
  • 调用工作类对象的工作函数,让这个函数执行业务

第二种方法更灵活,一个工作类可以有多个工作函数,多个工作对象也可以移动到同一个子线程中,可以人为控制指定线程执行指定任务。在第一种方法中,如果run函数需要传入的数据,可以通过信号槽机制+成员变量的形式实现接收,工作函数run函数是不能直接传入参数的;而在第二种方法中,工作函数是可以有参数的,可以通过信号槽机制直接传入数据,而不用再定义成员变量。
第一种方法简单,只需要书写线程类,重写run函数,然后在主线程中创建子线程对象,通过这个子线程对象调用start方法工作。弊端就是,如果需求、任务足够多,写入到run函数中,判定就复杂,run函数也越臃肿,因此在任务简单、少的情况下使用第一种方法

需要注意的一个点,使用connect(发出者,信号,接收者,处理函数)时,

connect(gen,&Generate::sendArray,bubble,&BubbleSort::working);
connect(gen,&Generate::sendArray,quick,&QuickSort::working);
//上下两种是不一样的,下面变成了在主线程里处理,因为bubble对象移动到了子线程里,
//所以bubble->working()也是在子线程下执行的
connect(gen,&Generate::sendArray,this,[=](QVector<int> list){bubble->working();quick->working();
});

三、线程资源释放

创建子线程对象时指定父对象,这样Qt就会自动回收这些资源,按照QT的内存回收机制,第一个是指定的父对象得是QObejct类的子类或者间接子类,第二个就是创建对象时必须指定父对象,父对象析构的时候会根据对象树先析构子对象
第二种方式在当前窗口类的析构函数中释放掉这些资源,就是手动析构。当然写的程序有时候析构函数是访问不到哪些子线程对象的,如果不想把这些子线程对象设置为窗口类的成员变量,那我们还可以通过信号-槽的机制来回收这些资源,窗口关闭时发出destroed的信号,这里利用信号槽,收到这个信号时释放掉子线程对象、任务对象的资源

    connect(this, &MainWindow::destroyed, this, [=](){t1->quit();t1->wait();t1->deleteLater();t2->quit();t2->wait();t2->deleteLater();t3->quit();t3->wait();t3->deleteLater();gen->deleteLater();  // delete t1;bubble->deleteLater();quick->deleteLater();});

四、线程池

Qt提供了QThreadPool线程池类,然后有一列的API

// 在每个Qt应用程序中都有一个全局的线程池对象, 通过这个函数直接访问这个对象
static QThreadPool * QThreadPool::globalInstance();
//调用start方法,把任务封装成QRunnable 对象,扔给线程池,线程池下的空闲线程就会帮我们处理对应任务
//QRunnable * runnable 任务的类型,QRunnable 主要是run函数和设置释放自动析构的API
//priority 优先级
void QThreadPool::start(QRunnable * runnable, int priority = 0);
  • 先创建一个要添加到线程池中的任务类,继承QObject(可以使用信号槽机制)、QRunnable
  • 重写run 方法(继承Qthread的run是protected,而QRunnable 的run是public),里面就是执行任务,另外还需要在构造函数里设置自动析构
  • 主函数中创建任务对象,调用QThreadPool::globalInstance()->start(task)执行任务;

五、基于线程池执行多任务

基于线程池可以将方法(1)的多线程修改为线程池实现多任务

参考博客


http://www.ppmy.cn/news/1537269.html

相关文章

大载重无人机物资吊运技术培训详解

大载重无人机物资吊运技术培训详解主要涉及理论知识、实操技能、安全规范以及应用领域等多个方面。以下是对这些方面的详细解析&#xff1a; 一、理论知识 1. 无人机基础知识 无人机类型与结构&#xff1a;了解大载重无人机的类型、结构特点及其工作原理&#xff0c;特别是针…

LeetCode-871 最低加油次数

重启力扣每日一题系列&#xff01; 因为过去两个月里掉粉掉的好严重&#xff0c;我想大抵是因为更新的频率不如上半年了&#xff0c;如果我重启了每日一题系列那岂不是至少是每日一更☝&#x1f913;&#xff1f; 也不是每天都更&#xff0c;我有两不更&#xff0c;特难的就不…

【重学 MySQL】四十七、表的操作技巧——修改、重命名、删除与清空

【重学 MySQL】四十七、表的操作技巧——修改、重命名、删除与清空 修改表添加字段语法示例注意事项 删除字段语法示例 修改字段使用 MODIFY COLUMN语法示例 使用 CHANGE COLUMN语法示例 重命名表语法示例 删除表语法示例 清空表使用 TRUNCATE TABLE使用 DELETE FROM对比 TRUNC…

如何在 SQL 中创建一个新的数据库?

在SQL中创建一个新的数据库&#xff0c;首先你需要有一个可以执行SQL语句的环境。 这通常意味着你已经有了一个数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;如MySQL、PostgreSQL、Oracle或Microsoft SQL Server等。 不同的DBMS可能有不同的细节&#xff0c;但基本…

MATLAB使用高斯消元法计算方程组的解

function X uptrbk(A,B) % A,B是系数矩阵和列向量 % 求方阵A 含多少行(列) X是N*1列向量解 [~, N] size(A); X zeros(N,1); % C一行,N1列 C zeros(1,N1); % 增广矩阵Aug Aug [A,B]; %循环从第一列到倒数第二列 for p1:N-1[~,j] max(abs(Aug(p:N,p)));%返回每一列中的绝对…

如何免费为域名申请一个企业邮箱

背景 做SEO的是有老是会有一些网站来做验证你的所有权&#xff0c;这个时候&#xff0c;如果你域名对应的企业邮箱就会很方便。zoho为了引导付费&#xff0c;有很多多余的步骤引导&#xff0c;反倒是让不付费的用户有些迷茫&#xff0c;所以会写这个教程&#xff0c;按照教程走…

使用纯CSS和JavaScript来实现一个转盘抽奖效果

使用纯CSS和JavaScript来实现一个转盘抽奖效果,抽到机率相同 <!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" />&…

手游和应用出海资讯:字节跳动《Lemon8》在美下载量飙升;美团海外版《Keeta》进军沙特市场

NetMarvel帮助游戏和应用广告主洞察全球市场、获取行业信息&#xff0c;以下为10月第一周资讯&#xff1a; ● OpenAI Sora负责人加盟 Google DeepMind ● 字节跳动《Lemon8》登顶美国App Store排行榜 ● 消息称腾讯与Guillemot家族考虑收购育碧 ● OpenAI官宣获66亿美元融资 ●…