概述

QThread类提供了一个与平台无关的管理线程的方法。一个QThread对象管理一个线程。QThread的执行从run()函数的执行开始,在Qt自带的QThread类中,run()函数通过调用exec()函数来启动事件循环机制,并且在线程内部处理Qt的事件。在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,从而让主界面能及时响应用户的请求操作。QThread的使用方法有如下两种:

  1. QObject::moveToThread()

  2. 继承QThread类

下面通过具体的方法描述和例子来介绍两种方法。

方法一. QObject::moveToThread()方法

方法描述

  1. 定义一个继承于QObject的worker类,在worker类中定义一个槽slot函数doWork(),这个函数中定义线程需要做的工作。
  2. 在要使用线程的controller类中,新建一个QThread的对象和woker类对象,使用moveToThread()方法将worker对象的事件循环全部交由QThread对象处理。
  3. 建立相关的信号函数和槽函数进行连接,然后发出信号触发QThread的槽函数,使其执行工作。

moveToThread的例子

首先新建一个work类,该类重点在于其doWork槽函数,这个函数定义了线程需要做的工作,需要向其发送信号来触发。Wrok类的头文件中定义了全部函数,其cpp文件为空,因此就不贴出来了。

Wroker.h的定义如下

// work定义了线程要执行的工作
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
#include<QDebug>
#include<QThread>class Worker:public QObject
{Q_OBJECT
public:Worker(QObject* parent = nullptr){}
public slots:// doWork定义了线程要执行的操作void doWork(int parameter){qDebug()<<"receive the execute signal---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId();// 循环一百万次for(int i = 0;i!=1000000;++i){++parameter;}// 发送结束信号qDebug()<<"      finish the work and sent the resultReady signal\n";emit resultReady(parameter);}// 线程完成工作时发送的信号
signals:void resultReady(const int result);
};#endif // WORKER_H

然后定义一个Controller类,这个类中定义了一个QThread对象,用于处理worker对象的事件循环工作。

controller.h的定义如下:

#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include<QThread>
#include<QDebug>// controller用于启动线程和处理线程执行结果
class Controller : public QObject
{Q_OBJECTQThread workerThread;
public:Controller(QObject *parent= nullptr);~Controller();public slots:// 处理线程执行的结果void handleResults(const int rslt){qDebug()<<"receive the resultReady signal---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';qDebug()<<"     the last result is:"<<rslt;}
signals:// 发送信号触发线程void operate(const int);};#endif // CONTROLLER_H

Controller类的cpp文件,其构造函数中创建worker对象,并且将其事件循环全部交给workerThread对象来处理,最后启动该线程,然后触发其事件处理函数。

controller.cpp的定义如下:

#include "controller.h"
#include <worker.h>
Controller::Controller(QObject *parent) : QObject(parent)
{Worker *worker = new Worker;//调用moveToThread将该任务交给workThreadworker->moveToThread(&workerThread);//operate信号发射后启动线程工作connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));//该线程结束时销毁connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);//线程结束后发送信号,对结果进行处理connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));//启动线程workerThread.start();//发射信号,开始执行qDebug()<<"emit the signal to execute!---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';emit operate(0);
}
//析构函数中调用quit()函数结束线程
Controller::~Controller()
{workerThread.quit();workerThread.wait();
}

接下来就是主函数,主函数中我们新建一个Controller对象,开始执行:

main.cpp的内容如下

#include <QCoreApplication>
#include "controller.h"
#include<QDebug>
#include<QThread>
int main(int argc, char *argv[])
{qDebug()<<"I am main Thread, my ID:"<<QThread::currentThreadId()<<"\n";QCoreApplication a(argc, argv);Controller c;return a.exec();
}

运行结果及说明

运行结果截图 1

main函数中打印当前线程编号,即主线程的线程编号是0X7a4, 在Controller的构造函数中继续打印当前线程编号,也是主线程编号,之后把work类的工作交给子线程后,给子线程发送信号,子线程收到了信号开始执行,其线程号为0X1218,执行结束后发送信号给Controller处理结果。

方法二. 继承QThread的方法

方法描述

  1. 自定义一个继承QThread的类MyThread,重载MyThread中的run()函数,在run()函数中写入需要执行的工作.
  2. 调用start()函数来启动线程。

继承QThread的例子

首先写MyThread类,该类继承于QThread,该类中自定义了信号槽和重写了run函数。头文件如下:

mythread.h内容如下

#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include<QDebug>
class MyThread : public QThread
{Q_OBJECT
public:MyThread(QObject* parent = nullptr);//自定义发送的信号
signals:void myThreadSignal(const int);//自定义槽
public slots:void myThreadSlot(const int);
protected:void run() override;
};#endif // MYTHREAD_H

mythread.cpp内容如下

#include "mythread.h"MyThread::MyThread(QObject *parent)
{}void MyThread::run()
{qDebug()<<"myThread run() start to execute";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';//循环一百万次int count = 0;for(int i = 0;i!=1000000;++i){++count;}// 发送结束信号emit myThreadSignal(count);exec();
}void MyThread::myThreadSlot(const int val)
{qDebug()<<"myThreadSlot() start to execute";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';// 循环一百万次int count = 888;for(int i = 0;i!=1000000;++i){++count;}
}

在Controller类中实现这MyThread的调用。

controller.h内容如下(评论区的老哥们指出图片贴错了,现在已改正。)

#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <QObject>
#include <QThread>
#include <QDebug>// controller用于启动线程和处理线程执行结果
class Controller : public QObject
{Q_OBJECTQThread* myThrd;
public:Controller(QObject *parent= nullptr);~Controller();public slots:// 处理线程执行的结果void handleResults(const int rslt){qDebug()<<"receive the resultReady signal---------------------------------";qDebug()<<"     current thread ID:"<<QThread::currentThreadId()<<'\n';qDebug()<<"     the last result is:"<<rslt;}
signals:// 发送信号触发线程void operate(const int);};#endif // CONTROLLER_H

controller.cpp内容如下

#include "controller.h"
#include "mythread.h"
Controller::Controller(QObject *parent) : QObject(parent)
{myThrd = new MyThread;connect(myThrd,SIGNAL(&MyThread::myThreadSignal),this, SLOT(&Controller::handleResults));// 该线程结束时销毁connect(myThrd, SIGNAL(&QThread::finished), this, SLOT(&QObject::deleteLater));connect(this,SIGNAL(&Controller::operate),myThrd, SLOT(&MyThread::myThreadSlot));// 启动该线程myThrd->start();QThread::sleep(5);emit operate(999);
}Controller::~Controller()
{myThrd->quit();myThrd->wait();
}

main函数的内容和上例中相同,因此就不贴了。

运行结果和说明:运行结果截图2

通过自定义一个继承QThread的类,实例化该类的对象,重载run()函数为需要做的工作。然后在需要的地方调用start函数来执行run函数中的任务。然而有趣的是,myThread.start()之后我又从主函数触发了一个信号,对应于子线程的槽,子线程的槽函数中打印当前执行的线程的编号,可以看到,执行子线程的槽函数的线程编号却是主线程的编号

两种方法的比较

两种方法来执行线程都可以,随便你的喜欢。不过看起来第二种更加简单,容易让人理解。不过我们的兴趣在于这两种使用方法到底有什么区别?其最大的区别在于:

  1. moveToThread方法,是把我们需要的工作全部封装在一个类中,将每个任务定义为一个的槽函数,再建立触发这些槽的信号,然后把信号和槽连接起来,最后将这个类调用moveToThread方法交给一个QThread对象,再调用QThread的start()函数使其全权处理事件循环。于是,任何时候我们需要让线程执行某个任务,只需要发出对应的信号就可以。其优点是我们可以在一个worker类中定义很多个需要做的工作,然后发出触发的信号线程就可以执行。相比于子类化的QThread只能执行run()函数中的任务,moveToThread的方法中一个线程可以做很多不同的工作(只要发出任务的对应的信号即可)。
  2. 子类化QThread的方法,就是重写了QThread中的run()函数,在run()函数中定义了需要的工作。这样的结果是,我们自定义的子线程调用start()函数后,便开始执行run()函数。如果在自定义的线程类中定义相关槽函数,那么这些槽函数不会由子类化的QThread自身事件循环所执行,而是由该子线程的拥有者所在线程(一般都是主线程)来执行。如果你不明白的话,请看,第二个例子中,子类化的线程的槽函数中输出当前线程的ID,而这个ID居然是主线程的ID!!事实的确是如此,子类化的QThread只能执行run()函数中的任务直到run()函数退出,而它的槽函数根本不会被自己的线程执行。

PS:
       以上代码是Qt5.7开发环境,采用的是VS2015的64位编译器。代码可以直接复制粘贴运行

QThread的用法相关推荐

  1. QThread moveToThread用法总结

    方案1 Qt中创建独立的线程,一般都是采用继承QThread类,然后重写run虚函数,在run函数中执行任务. class CThread: public QThread {Q_OBJECT publ ...

  2. Qt——线程类QThread

    本文主要介绍Qt中线程类QThread的用法,参考(翻译+修改)了一篇文章:PyQt: Threading Basics Tutorial,虽然使用的是PyQt,但与C++中Qt的用法大同小异,不必太 ...

  3. 【转】【QT】 Threads, Events and QObjects

    前言: qt wiki 中这篇文章3月份再次更新,文章对 QThread 的用法,使用场景,有很好的论述,可以作为 Qt 多线程编程的使用指南,原文在这里,原作者 peppe 开的讨论贴在这里. 原文 ...

  4. Qt中使用线程的几种方式及区别

    概述 Qt中有多种创建线程的方式,每一种的应用场景和使用方式都有些区别, 这里主要简单介绍Qt里面的几大创建线程的方法,以及使用注意事项. QThread 使用QThread创建线程是我们最常见的一种 ...

  5. python+PyQt5实现文件安全传输

    实验目的 设计安全的信息传输工具,解决网络传输涉密文件过程中的安全性问题.安全的信息传输,涉及多个密码学知识点,在实验设计过程中,不断加深理解密码学基本概念和算法基础原理,并且能够锻炼独立的代码编写能 ...

  6. Qt之QThread用法

    QThread类提供了与系统无关的线程. QThread代表在程序中一个单独的线程控制.线程在run()中开始执行,默认情况下,run()通过调用exec()启动事件循环并在线程里运行一个Qt的事件循 ...

  7. QThread、moveToThread用法详述

    1.吐槽 QThread类提供了一种平台无关的方法对线程进行管理.但对于QThread类的熟练使用,即使是从事Qt开发多年的程序猿们,往往也会踩雷.入坑.总之:QThread类不好用.如果对该类理解不 ...

  8. QThread之moveToThread用法

    一.怎么用 使用一个QObject作为Worker,并moveToThread到线程上,那么这个QObject生存在此线程上,其信号会在此线程上发射,其槽函数在此线程上执行. 意味着什么,意味着多线程 ...

  9. pyqt5 使用 QTimer, QThread, pyqtSignal 实现自动执行,多线程,自定义信号触发。

    渣渣用法,请等待我心情好的时候更新. 1.第一个例子 1.1 先看mainwindow.py from PyQt5 import QtCore, QtGui, QtWidgetsclass Ui_Ma ...

  10. Qt中使用多线程的一些心得(一)——继承QThread的多线程使用方法

    一 前言 二Qt多线程方法一 继承QThread 2.1使用多线程的原因分析 2.2 写一个继承于QThread的线程 三 总结 一 前言   本篇文章部分内容参考了该博文:传送门.   Qt中有两种 ...

最新文章

  1. ftrace跟踪内核_ftrace、kpatch、systemtap的基本原理、联系和区别
  2. 对学校的希望和寄语_家长对学校的寄语怎么写
  3. 学习Kotlin(三)类和接口
  4. python调用其他文件的类和函数
  5. bat常用命令操作符列表
  6. A*B Problem
  7. java分页模板_java 分页模型的模板
  8. Spring IOC容器-自动装配
  9. 【Flink】flink keyby 在 subtask 中分配不均的研究
  10. 使用PHP的“注意:未定义的变量”,“注意:未定义的索引”和“注意:未定义的偏移量”
  11. base——JavaSEJavaEEJavaME的区别【Java中常用的包结构】
  12. 像差与zernike多项式
  13. 我的博客文章快速索引
  14. 《凤凰项目》读书笔记一
  15. UML用例图的画法详细介绍【软件工程】
  16. P2906 [USACO08OPEN]牛的街区Cow Neighborhoods
  17. 如东人院附近一网吧转让
  18. python创建表格怎么只能65536_python - 在Python和Excel 2010中使用Win32时如何克服65536 Excel行限制 - 堆栈内存溢出...
  19. php5.3 发送邮件phpemail的使用 (适用php5.3)
  20. c++ 对注册表启动项写入

热门文章

  1. webservice框架jersey简单总结
  2. 网吧用电影服务器系统,网吧流媒体电影服务器搭建的解决方案
  3. java word模板生成pdf,java根据模板生成pdf
  4. Python3入门精通基础教程(合集)
  5. 国科大计算机算法与分析——陈玉福 马菲菲
  6. 仿美团外卖源码加自己做了个模拟数据加载的效果
  7. 企业如何布局数字化营销,打造私域运营闭环实现增长?
  8. VARCHAR2 与 NVARCHAR2 区别
  9. laravel框架简单总结
  10. 亦是美网络,致力于操作系统应用与计算机网络技术的IT网站。