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

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

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

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

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

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

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

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

第二种方法更灵活,一个工作类可以有多个工作函数,多个工作对象也可以移动到同一个子线程中,可以人为控制指定线程执行指定任务。在第一种方法中,如果run函数需要传入的数据,可以通过信号槽机制+成员变量的形式实现接收,工作函数run函数是不能直接传入参数的;而在第二种方法中,工作函数是可以有参数的,可以通过信号槽机制直接传入数据,而不用再定义成员变量。

第一种方法简单,只需要书写线程类,重写run函数,然后在主线程中创建子线程对象,通过这个子线程对象调用start方法工作。弊端就是,如果需求、任务足够多,写入到run函数中,判定就复杂,run函数也越臃肿,因此在任务简单、少的情况下使用第一种方法

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

c 复制代码
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的信号,这里利用信号槽,收到这个信号时释放掉子线程对象、任务对象的资源

c 复制代码
    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

c 复制代码
// 在每个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)的多线程修改为线程池实现多任务

参考博客

相关推荐
----云烟----9 小时前
QT中QString类的各种使用
开发语言·qt
「QT(C++)开发工程师」14 小时前
【qt版本概述】
开发语言·qt
一路冰雨18 小时前
Qt打开文件对话框选择文件之后弹出两次
开发语言·qt
老赵的博客19 小时前
QT 自定义界面布局要诀
开发语言·qt
码码哈哈0.019 小时前
VSCode 2022 离线安装插件QT VSTOOl报错此扩展不能安装在任何当前安装的产品上。
ide·vscode·qt
&梧桐树夏1 天前
JavaEE 线程安全
java-ee·多线程
feiyangqingyun1 天前
Qt/C++离线地图的加载和交互/可以离线使用/百度和天地图离线/支持手机上运行
c++·qt·qt天地图·qt离线地图·qt地图导航
程序猿小D1 天前
第三百三十节 Java网络教程 - Java网络UDP服务器
java·开发语言·网络·网络协议·udp·多线程
gz94562 天前
windows下,用CMake编译qt项目,出现错误By not providing “FindQt5.cmake“...
开发语言·qt
「QT(C++)开发工程师」2 天前
Ubuntu 26.04 LTS 大升级:Qt 6 成为未来新引擎
qt