QThread的用法

article/2025/10/16 11:14:04

概述

       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的对象和worker类对象,使用moveToThread()方法将worker对象的事件循环全部交由QThread对象处理。(转移到线程中)
  3. 建立相关的信号函数和槽函数进行连接,然后发出信号触发QThread的槽函数,使其执行工作。

moveToThread的例子

       首先新建一个worker类,该类重点在于其doWork槽函数,这个函数定义了线程需要做的工作,需要向其发送信号来触发。Work类的头文件中定义了全部函数,其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的构造函数中继续打印当前线程编号,也是主线程编号,之后把worker类的工作交给子线程后,给子线程发送信号,子线程收到了信号开始执行,其线程号为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的用法_给我爬!的博客-CSDN博客_qthread


http://chatgpt.dhexx.cn/article/8DS7yUpG.shtml

相关文章

Oracle 定时任务执行存储过程【建议收藏】

首先用一个完整的例子来实现定时执行存储过程。 任务目标&#xff1a;每小时向test表中插入一条数据。 实现方案&#xff1a; 1.通过 oracle 中 dbms_job 完成存储过程的定时调用 2.在存储过程中完成相应的逻辑操作 实现步骤&#xff1a; 1.创建一个测试表 create table test…

【Mysql】MySQL 用户执行存储过程的权限

问题 运行存储过程报错&#xff1a; 原因 查询资料&#xff1a; 1305错误&#xff0c;由于当前用户没用权限&#xff0c;对用户进行授权后可以执行。 解决 MySQL创建存储过程/函数需要的权限&#xff1a; alter routine---修改与删除存储过程/函数 create routine--创建…

goland 使用 gorm 执行 存储过程 : go语言 执行存储过程

使用 gorm 执行 存储过程 初安装依赖代码&#xff1a; 附存储过程图片存储过程代码&#xff08;创建&#xff09; 表结构表结构图表结构代码 初 最近遇到要写存储过程需求&#xff0c;使用 大佬写的 框架 gorm 来完成。简直是方便的不行&#xff1a; 直接上代码&#xff1a; …

JDBC之CallableStatement执行存储过程

​ 在前面的一篇文章中&#xff0c;我们学习使用Statement、PreparedStatement来完成对数据表的增删改查。而存储过程作为数据库的重要组成部分&#xff08;痛点&#xff0c;当时学的时候头发都掉了好几根&#x1f62d;&#xff09;&#xff0c;那JDBC是如何执行存储过程呢&…

mysql创建定时任务执行存储过程

存储过程已添加好&#xff1a;https://blog.csdn.net/YXWik/article/details/127283316 1.创建定时器用来执行存储过程函数 create event delete_data on schedule every 10 second do call delete_data();这里的第一行代表的创建名称为delete_data的事件 第二行是执行周期为…

Oracle 定时任务执行存储过程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、创建存储过程一、创建定时任务 一、创建存储过程 CREATE OR REPLACE PROCEDURE TESTCREATE AS --存储过程名称I INT : 1; --变量 BEGINWHILE I < 10 LOOP -…

oracle执行存储过程参数,Oracle 执行存储过程四种方法(带参数 不带参数)

1.如果是命令窗口就用exec 存储过程名&#xff1a; 1 EXEC procedure;--procedure是存储过程名 2.如果是 SQL窗口就用 begin 存储过程名 end; 1 2 3 begin procedure;--procedure是存储过程名 end; 3.如果是程序中调用就用 call 存储过程名 &#xff0c;举个栗子&#xff1…

plsql定时执行存储过程

1、创建定时向表中插入数据的存储过程&#xff0c;名为testJob。 CREATE OR REPLACE PROCEDURE "testJob" AS BEGIN EXECUTE IMMEDIATE INSERT INTO TABLE_HIS SELECT * FROM TABLE_AI; COMMIT; END; 2、使用plsql找到定时器对应的DBMS_Jobs文件夹&#xff0c;…

SQLserver存储过程简单写法与设置定时执行存储过程方法

最近工作中需要写SQLserver的存储过程&#xff0c;第一次使用&#xff0c;简单记录下&#xff0c;以防遗忘。 在SQLserver可视化工具中编写&#xff0c;我的工具如下图&#xff1a; 首先点击你的数据库&#xff0c;找到可编程性&#xff0c;在可编程性里面右击存储过程-->点…

symlink() 函数

查看更多 https://www.yuque.com/docs/share/10f959a4-bd7e-47a9-ad78-11a1310613f3

Install fail! Error: EPERM: operation not permitted, symlink

这个是在安装lodash的时候报错的情况 尝试了下方命令&#xff0c;但是还是有误 在这里可能是因为npm缓存的问题&#xff0c;我先删除了 C:\Users\abc(自己电脑的用户名) 文件夹下的 .npmrc 文件&#xff0c;但是没反应。然后运行了下方的命令&#xff1a; 运行&#xff1a;…

error: eperm: operation not permitted, symlink

查询当前配置的镜像 npm get registry > https://registry.npmjs.org/ 设置成淘宝镜像 npm config set registry http://registry.npm.taobao.org/

Linux Symbolic Links(软链接)

linux 中的链接有两种&#xff0c;分别是 Hard Links&#xff08;硬链接&#xff09;和Symbolic Links(软链接)。 1. 什么是硬链接、软链接&#xff1f; 硬链接&#xff1a;在另外一个位置创建源文件的链接文件&#xff0c;相当于复制了一份&#xff0c;占用资源会倍增。硬链…

linux link/symlink/unlink 硬连接和软连接介绍

文章目录 硬连接和软连接的区别硬连接软连接 链接命令lnln指令参数含义例子 硬链接函数link()符号链接函数symlink()解除链接函数unlink() link/symlink/unlink函数头文件为#include <unistd.h> 硬连接和软连接的区别 硬连接 硬连接指通过索引节点来进行连接。 在Lin…

​2021-12-13 Android 的 init.rc 文件​里面的symlink,把<target>链接到目录<path>下。

一、symlink <target> <path>&#xff0c;把<target>链接到目录<path>下。 二、来看一下实际的例子 三、参考文章&#xff1a; Android 的 init.rc 文件简介 &#xff08;转&#xff09; - 陈wei的个人空间 - OSCHINA - 中文开源技…

pnpm安装使用教程以及pnpm node版本管理以及EPERM operation not permitted symlink问题解决

pnpm安装使用教程以及pnpm node版本管理以及EPERM operation not permitted symlink问题解决 pnpm&#xff1a;目前来看是一个大趋势&#xff0c;如&#xff1a;nest-cli目前除了npm、yarn增加了pnpm 优势&#xff1a;节约磁盘空间并提升安装速度&#xff08;改善node_modules…

Cannot create symlink/symbolic to `xxx': Operation not supported

1、Cannot create symlink to xxx: Operation not supported 在虚拟机的共享目录中&#xff0c;解压内核源码&#xff0c;试过了几种解压方法都报出如下错误&#xff1a; tar: linux-4.15/tools/testing/selftests/powerpc/vphn/vphn.h: Cannot create symlink to ../../../.…

Flutter报错Building with plugins requires symlink support的解决方法

错误 Building with plugins requires symlink support. Please enable Developer Mode in your system settings. Runstart ms-settings:developers to open settings. 原因 这个错误表示你的系统尚未启用开发者模式,所以无法使用Flutter的插件功能。 Flutter插件会通过符号…

硬链接,软链接,link,rename,symlink,opendir和readdir

什么是硬链接 struct stat {nlink_t st_nlink; /* Number of hard links};stat结构体就有一个成员变量----硬链接数 使用ln命令就可以创建硬链接 创建硬链接&#xff0c;就是再为文件创建一个名字 每创建一个硬链接&#xff0c;文件就多一个文件名&#xff0c;硬件链…

符号链接symlink_什么是符号链接或符号链接? 如何为Windows和Linux创建Symlink?

符号链接symlink Symbolic Links are also known as Symlinks. Symlinks are used to create a shortcut for a given file or folder. Symlinks are very useful for different purposes which also prevents copy the same data over and over again. In this tutorial, we w…