Qt 多线程基础及线程使用方式

Qt 多线程基础及线程使用方式文章目录 Qt 多线程操作 2 线程类 QThread3 多线程使用 方式一 4 多线程使用 方式二 5 Qt 线程池的使用 Qt 多线程操作应用程序在某些情况下需要处理比较复杂的逻辑 如果只有一个线程去处理 就会导致窗口卡顿 无法处理用户的相关操作 这种情况下就需要使用多线程 其中一个线程处理窗口事件 其他线程进行逻辑运算 多个线程各司其职 不仅可以提高用户体验还可以提升程序的执行效率 Qt 中使用多线程需要注意 Qt 的默认线程为窗口线程 主线程 负责窗口事件处理或窗口控件数据的更新 子线程负责后台的业

Qt 多线程操作

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

Qt中使用多线程需要注意:

  1. Qt的默认线程为窗口线程(主线程):负责窗口事件处理或窗口控件数据的更新;
  2. 子线程负责后台的业务逻辑,子线程不能对窗口对象做任何操作,这些事交给窗口线程;
  3. 主线程和子线程之间如果进行数据的传递,需要使用信号槽机制
2.线程类QThread

Qt 中提供了一个线程类,通过这个类就可以创建子线程了,Qt 中一共提供了两种创建子线程的方式。

类中常用API函数:

//QThead类常用 API //构造函数 QThread::QThread(QObject *parent = Q_NULLPTR); //判断线程中的任务是否处理完毕 bool QThread::isFinished() const; //判断子线程是不是在执行任务 bool QThread::isRunning() const; //Qt 可先设置线程优先级 //获取当前线程优先级 Priority QThread::priority() const; //设置优先级 void QThread::setPriority(Priority priority); //枚举类型 //退出线程的工作函数 void QThread::exit(int returnCode = 0); //调用线程退出函数之后,线程不会马上退出,因为当前任务可能没有完成 //等待任务完成后退出线程,通常在exit() 后调用这个函数 bool QThread::wait(unsigned long time = ULONG_MAX); 

优先级:
QThread::IdlePriority –> 最低的优先级
QThread::LowestPriority
QThread::LowPriority
QThread::NormalPriority
QThread::HighPriority
QThread::HighestPriority
QThread::TimeCriticalPriority
QThread::InheritPriority –> 最高的优先级, 默认是这个
















信号槽:

// 和调用 exit() 效果是一样的 // 代用这个函数之后, 再调用 wait() 函数 [slot] void QThread::quit(); // 启动子线程 [slot] void QThread::start(Priority priority = InheritPriority); // 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数 [slot] void QThread::terminate(); // 线程中执行的任务完成了, 发出该信号 // 任务函数中的处理逻辑执行完毕了 [signal] void QThread::finished(); // 开始工作之前发出这个信号, 一般不使用 [signal] void QThread::started(); 

静态函数

// 返回一个指向管理当前执行线程的QThread的指针 [static] QThread *QThread::currentThread(); // 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同 [static] int QThread::idealThreadCount(); // 线程休眠函数 [static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒 [static] void QThread::sleep(unsigned long secs); // 单位: 秒 [static] void QThread::usleep(unsigned long usecs); // 单位: 微秒 

任务处理函数

// 子线程要处理什么任务, 需要写到 run() 中 [virtual protected] void QThread::run(); 

run()是一个虚函数,如果想让创建的子线程执行某个任务,需要写一个子类让其继承QThread,并且在子类中重写父类的run()方法,函数体就是任务处理流程。

run()函数是受保护的成员函数,不能类外调用,如果需要通过当前线程对象调用槽函数start()启动子线程,启动后run()函数在线程内部被调用。

3.多线程使用:方式一

1.创建一个线程类的子对象,继承QThread

class MyThrad:public QThread { ...... } 

2.重写父类的run()方法,在该函数内部编写子线程要处理的具体业务流程

class MyThread:public QThread { protected: void run() { .........; } } 

3.在主线程中创建子线程对象,new一个

MyThread * subThread = new MyThrad; 

4.启动子线程,调用start()方法

subThread->start(); 

不能在类的外部调用run()方法启动子对象,在外部使用start()相当于run()

创建出子线程后,父子线程之间的通信可以通过信号槽的方式,注意:

Qt子线程对象不能操作窗口类型对象,只有主线程才能操作窗口对象。

5.释放线程资源,使用信号槽机制,窗口关闭时释放

connect(this,&QWidget::destroyed,this,[=](){ //t1 为创建的子线程对象 gen->quit(); gen->wait(); gen->deleteLater(); }); 
4.多线程使用:方式二

Qt提高的第二种线程的创建方式弥补了第一种的缺点==,使用更加灵活,单写起来相对复杂一些==。

具体操作步骤:

1.创建一个新的类,QObject派生

class Mywork :public QObject { ....; } 

2.类中添加一个公有的自定义成员函数,函数体就是子线程中执行的业务逻辑

class Mywork :public QObject { public: //自定义函数名、参数 void working(); } 

3.主线程中创建一个 QThread 对象,就是子线程对象

QThread *sub = new QThread ; 

4.在主线程中创建工作的类对象,不要给创建的对象指定父对象

// MyWork* work = new MyWork(this); Mywork* work = new Mywork; // ok 

5.将Mywork对象移动到创建的子线程对象中,需要调用QObject类提供的 moveToThread() 方法

/ 如果给work指定了父对象, 这个函数调用就失败了 // 提示: QObject::moveToThread: Cannot move objects with a parent work->moveToThread(sub); // 移动到子线程中工作s 

启动子线程,调用 start(), 这时候线程启动了,但是移动到线程中的对象并没有工作

调用 MyWork 类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的

6.释放线程资源,使用信号槽机制,窗口关闭时释放

//信号槽释放资源 connect(this,&QWidget::destroyed,this,[=](){ //释放创建的子线程对象 ti->quit(); t1->wait(); t1->deleteLater(); //释放创建的任务对象 gen->deleteLater(); }); 
5.Qt 线程池的使用

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

线程池的组成:

1.任务队列:储存需要处理的任务,由工作的线程来处理这些任务

​ 通过线程池提供的API函数,将一个待处理的任务添加到任务列表,或者从任务列表队伍中删除,已处理的任务会被从任务队列中删除

​ 线程池的使用者,及往任务队列添加任务的线程就是生产者线程

2.工作的线程:(任务队列中的消费者),N个

​ 线程池中维护一定数量的工作线程,作用是不停的读任务队列,从里面取出任务并处理

如果任务队列为空,工作的线程将会被阻塞(使用条件变量/信号量阻塞)

​ 如果阻塞之后有了新的任务,由生产者将阻塞解除,工作线程开始工作

2.管理者线程:(不处理任务队列中的任务),1个

​ 作用:周期性的对任务队列中的任务数量以及处于忙状态的工作线程个数进行检测

​ 任务过多时,适当创建新的工作线程

​ 任务过少时,适当销毁一些工作线程

QRunnable

使用线程池需要先创建任务,添加到线程池中的任务 需要QRunnable类型因此需要创建子类继承QRunnable类,然后重写run()方法,在该函数编写在线程池中执行的任务,并将子类对象传递给线程池,这样线程就可以被线程池中的某个工作线程处理掉。

QRunnable常用函数:

// 在子类中必须要重写的函数, 里边是任务的处理流程 [pure virtual] void QRunnable::run(); // 参数设置为 true: 这个任务对象在线程池中的线程中处理完毕, 这个任务对象就会自动销毁 // 参数设置为 false: 这个任务对象在线程池中的线程中处理完毕, 对象需要程序猿手动销毁 void QRunnable::setAutoDelete(bool autoDelete); // 获取当前任务对象的析构方式,返回true->自动析构, 返回false->手动析构 bool QRunnable::autoDelete() const; 

1.创建一个要添加到线程池中的任务类

class MyWork:public QObject , public QRunnable { Q_OBJECT public: explicit MyWork (QObject *parent = nullptr) { //执行任务完毕,该对象自动销毁 setAutoDelete(true); } ~MyWork(); void run() override(); } 

在上面的示例中 MyWork 类是一个多重继承,如果需要在这个任务中使用 Qt 的信号槽机制进行数据的传递就必须继承 QObject 这个类,如果不使用信号槽传递数据就可以不继承了,只继承 QRunnable 即可。

QThreadPool

Qt 中的 QThreadPool 类管理了一组 QThreads, 里边还维护了一个任务队列。QThreadPool 管理和回收各个 QThread 对象,以帮助减少使用线程的程序中的线程创建成本。==每个Qt应用程序都有一个全局 QThreadPool 对象,可以通过调用 globalInstance() 来访问它。==也可以单独创建一个 QThreadPool 对象使用。

线程池常用API函数:

// 获取和设置线程中的最大线程个数 int maxThreadCount() const; void setMaxThreadCount(int maxThreadCount); // 给线程池添加任务, 任务是一个 QRunnable 类型的对象 // 如果线程池中没有空闲的线程了, 任务会放到任务队列中, 等待线程处理 void QThreadPool::start(QRunnable * runnable, int priority = 0); // 如果线程池中没有空闲的线程了, 直接返回值, 任务添加失败, 任务不会添加到任务队列中 bool QThreadPool::tryStart(QRunnable * runnable); // 线程池中被激活的线程的个数(正在工作的线程个数) int QThreadPool::activeThreadCount() const; // 尝试性的将某一个任务从线程池的任务队列中删除, 如果任务已经开始执行就无法删除了 bool QThreadPool::tryTake(QRunnable *runnable); // 将线程池中的任务队列里边没有开始处理的所有任务删除, 如果已经开始处理了就无法通过该函数删除了 void QThreadPool::clear(); // 在每个Qt应用程序中都有一个全局的线程池对象, 通过这个函数直接访问这个对象 static QThreadPool * QThreadPool::globalInstance(); 

runnable);

 一般情况下,我们不需要在 Qt 程序中创建线程池对象,直接使用 Qt 为每个应用程序提供的线程池全局对象即可。得到线程池对象之后,调用 start() 方法就可以将一个任务添加到线程池中,这个任务就可以被线程池内部的线程池处理掉了,使用线程池比自己创建线程的这种多种多线程方式更加简单和易于维护。 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/215829.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午1:04
下一篇 2026年3月18日 下午1:04


相关推荐

  • 记一次使用 Lombok 翻车造成的事故!

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:liuxuzxx https://juejin.im/post/6881432532332576781 序言 …

    2021年6月27日
    129
  • mac idea 2021激活码-激活码分享

    (mac idea 2021激活码)2021最新分享一个能用的的激活码出来,希望能帮到需要激活的朋友。目前这个是能用的,但是用的人多了之后也会失效,会不定时更新的,大家持续关注此网站~IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.html…

    2022年3月21日
    150
  • Spring纯注解开发上篇(包含Component、Configuration、ComponentScan、Scope、PostConstruct、PreDestroy注解)

    Spring纯注解开发上篇(包含Component、Configuration、ComponentScan、Scope、PostConstruct、PreDestroy注解)注解开发定义 bean 顺序 1 在所有需要放到容器中的类 在类上使用 Component 注解 Component bookDao publicclassB Component 如果没有指定 id 则使用类名首字母小写做为 id 即 bookServiceI

    2026年3月19日
    3
  • php模板机制,ECMall模板解析语法与机制

    php模板机制,ECMall模板解析语法与机制马上注册 学习更多技术 享用更多经验 获取更多辅助工具 您需要登录才可以下载或查看 没有帐号 立即注册 x 在 ECMall 模板中 用 开头 以 结尾就构成一个标签单元 紧接着的单词就是标签名 在标签单元中单词前含 美元符 的为变量名 资源引用 res 标签功能 返回当前模板当前风格目录的 url 路径实例 resfile css ecmall css 这个标签在模板编译后将变成 ht

    2026年3月20日
    2
  • matlab粒子群算法多维函数最优解_多目标粒子群算法matlab

    matlab粒子群算法多维函数最优解_多目标粒子群算法matlab前面一篇文章介绍了遗传算法,这里再介绍一种进化算法,称为粒子群算法。同遗传算法类似,粒子群算法也是仿照了自然界的生物现象得到的。这种现象就是鸟群在某个未知空间内寻找食物这一思想。鸟群通过自身经验和种群之间的交流调整自己的搜寻路径,从而不断寻找,直到找到食物最多的地点。其中某个个体就是鸟群中某一只鸟,该个体具有的属性就是其所在位置和搜索方向。该个体所在地点对应的食物数量可以理解为该处函数取值的理想…

    2022年5月3日
    35
  • 职称计算机模块intern,职称计算机考试模块试题.pdf

    职称计算机模块intern,职称计算机考试模块试题.pdf职称计算机考试模块试题职称计算机考试WORD模块试题2008-02-0819:12职称计算机考试,找点题目看看,也不知道是不是就考这些,顺便给大家分享分享.1、新建一word文档,将Windows剪贴板上的内容粘贴到该Word文档中。2、保存当前文档的版本(不输入版本的备注),并设置关闭文档时自动保存版本。3、请用文档结构图显示当前文档,并设置为蓝底白字。4、请将WORD…

    2022年6月2日
    32

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号