QT开发:详解 Qt 多线程编程核心类 QThread:基本概念与使用方法

news2024/11/16 4:36:30

1. 引言

        在现代应用程序开发中,多线程编程是一个关键技术,能够显著提高程序的效率和响应速度。Qt 是一个跨平台的 C++ 框架,其中 QThread 类是实现多线程编程的核心类。本文将深入详解 QThread 的基本概念、使用方法及其在实际应用中的重要性。

2. 基本概念

2.1 什么是 QThread?

        QThread 是 Qt 框架中的一个类,用于创建和管理线程。与标准库中的 std::thread 类似,QThread 提供了一种机制来执行并发任务,但它不仅限于此。QThread 还集成了 Qt 的信号和槽机制,使得线程间的通信更加方便和高效。

2.2 线程的生命周期

        一个线程的生命周期包括创建、启动、执行、结束几个阶段。QThread 类提供了一系列方法来控制和管理这些阶段:

  • start(): 启动线程,调用 run() 方法。
  • run(): 线程的工作函数,通常需要重载。
  • quit(): 让线程退出事件循环,但不终止线程。
  • terminate(): 强制终止线程,不推荐使用。
  • wait(): 等待线程结束。

3. 使用方法

3.1 继承 QThread 类

最常见的使用方式是通过继承 QThread 类并重载其 run() 方法。

3.1.1 示例代码

为了确保代码文件内容正确,请参考以下内容:

首先我们需要自定添加一个MyThread 类,会自动生成头文件和源文件,文件中代码会自动生成基础部分,其他需要手动编写,如下:

项目目录结构如下所示:

/my_project
  ├── main.cpp
  ├── mythread.h
  ├── mythread.cpp
  └── my_project.pro

mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QDebug>

class MyThread : public QThread {
    Q_OBJECT

public:
    MyThread() = default;
    ~MyThread() = default;

protected:
    void run() override {
        // 这里是线程的主要工作
        for (int i = 0; i < 5; ++i) {
            qDebug() << "Thread running:" << i;
            QThread::sleep(1);
        }
    }
};

#endif // MYTHREAD_H

mythread.cpp

#include "mythread.h"
//这里仅做简单示例

 

main.cpp如下: 


int main() {
    MyThread thread;
    thread.start();
    thread.wait(); // 等待线程结束
    return 0;
}

运行结果如下:

 

3.1.2 知识精华

上面是一个简单 的 QThread 类应用示例,这里需要说明的一点是:

        在 Qt 项目中,.pro 文件是项目配置的重要部分,它不会自动生成,你需要手动创建它并添加相应的配置。如果你使用的是 Qt Creator 工具,它会自动为你生成一个初始的 .pro 文件,但你需要根据项目的需要手动修改和添加内容。

上面示例需要手动在.pro 文件中添加和修改配置项:

QT += core
QT -= gui

CONFIG += console
CONFIG -= app_bundle

TEMPLATE = app

SOURCES += main.cpp \
           mythread.cpp

HEADERS += mythread.h

如果没有手动添加以上配置,Qt Creator工具中是无法构建运行的。当前示例.pro文件如下

3.2 使用 Worker-Object 模式

        继承 QThread 并重载 run() 方法虽然简单直观,但并不是最佳实践。更推荐的方式是使用 Worker-Object 模式,将工作对象移到新线程中。

#include <QObject>
#include <QThread>
#include <QDebug>

// Worker类继承自QObject,用于执行线程中的工作任务
class Worker : public QObject {
    Q_OBJECT

public slots:
    // 槽函数,执行工作任务
    void doWork() {
        // 模拟耗时操作,每次循环等待1秒
        for (int i = 0; i < 5; ++i) {
            qDebug() << "Worker running:" << i;
            QThread::sleep(1); // 线程休眠1秒
        }
    }
};

int main() {
    // 创建一个QThread对象,用于管理新线程
    QThread thread;

    // 创建一个Worker对象,执行具体的工作任务
    Worker worker;

    // 将Worker对象移到新线程中运行
    worker.moveToThread(&thread);

    // 连接QThread的started信号和Worker的doWork槽
    // 当线程启动时,将调用Worker的doWork槽函数
    QObject::connect(&thread, &QThread::started, &worker, &Worker::doWork);

    // 连接Worker的doWork槽函数和QThread的quit槽
    // 当Worker完成工作后,将调用QThread的quit槽函数,退出线程事件循环
    QObject::connect(&worker, &Worker::doWork, &thread, &QThread::quit);

    // 连接QThread的finished信号和Worker的deleteLater槽
    // 当线程结束时,将调用Worker的deleteLater槽函数,删除Worker对象
    QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);

    // 启动线程,开始执行工作任务
    thread.start();

    // 等待线程结束
    thread.wait();

    // 返回0,表示程序正常结束
    return 0;
}

Worker 类定义

  • Worker 类继承自 QObject
  • doWork 是一个槽函数,用于执行具体的工作任务。在这里,它模拟耗时操作,通过循环和线程休眠来演示。

 main 函数

  • 创建一个 QThread 对象 thread,用于管理新线程。
  • 创建一个 Worker 对象 worker,用于执行具体的工作任务。
  • 使用 moveToThread 方法将 Worker 对象移到新线程中运行。
  • 使用 QObject::connect 方法连接信号和槽:
    • 当线程启动时,调用 Worker 的 doWork 槽函数。
    • 当 Worker 完成工作后,调用 QThread 的 quit 槽函数,退出事件循环。
    • 当线程结束时,调用 Worker 的 deleteLater 槽函数,删除 Worker 对象。
  • 调用 thread.start() 启动线程,开始执行工作任务。
  • 调用 thread.wait() 等待线程结束。
  • 返回 0,表示程序正常结束。

4. 信号和槽

QThread 充分利用了 Qt 的信号和槽机制,使得线程间通信更加方便和安全。

#include <QObject>
#include <QThread>
#include <QDebug>

// Worker类继承自QObject,用于执行线程中的工作任务
class Worker : public QObject {
    Q_OBJECT

public slots:
    // 槽函数,执行工作任务
    void doWork() {
        // 这里是工作任务的代码
        emit workDone(); // 任务完成后发出信号
    }

signals:
    // 信号,表示工作已经完成
    void workDone();
};

int main() {
    // 创建一个QThread对象,用于管理新线程
    QThread thread;

    // 创建一个Worker对象,执行具体的工作任务
    Worker worker;

    // 将Worker对象移到新线程中运行
    worker.moveToThread(&thread);

    // 连接Worker的workDone信号和QThread的quit槽
    // 当Worker完成工作后,将调用QThread的quit槽函数,退出线程事件循环
    QObject::connect(&worker, &Worker::workDone, &thread, &QThread::quit);

    // 连接QThread的finished信号和Worker的deleteLater槽
    // 当线程结束时,将调用Worker的deleteLater槽函数,删除Worker对象
    QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);

    // 启动线程,开始执行工作任务
    thread.start();

    // 在主线程中等待新线程结束
    thread.wait();

    // 返回0,表示程序正常结束
    return 0;
}

Worker 类定义

class Worker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {
        emit workDone(); // 任务完成后发出信号
    }

signals:
    void workDone(); // 信号,表示工作已经完成
};
  • Worker 类继承自 QObject
  • doWork 是一个槽函数,用于执行具体的工作任务。在这里,它发出 workDone 信号,表示任务已经完成。
  • workDone 是一个信号,用于通知外部任务已经完成。

main 函数

int main() {
    QThread thread;
    Worker worker;

    worker.moveToThread(&thread);

    QObject::connect(&worker, &Worker::workDone, &thread, &QThread::quit);
    QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);

    thread.start();
    thread.wait(); // 等待线程结束
    return 0;
}
  • 创建一个 QThread 对象 thread,用于管理新线程。
  • 创建一个 Worker 对象 worker,用于执行具体的工作任务。
  • 使用 moveToThread 方法将 Worker 对象移到新线程中运行。
  • 使用 QObject::connect 方法连接信号和槽:
    • 当 Worker 发出 workDone 信号时,调用 QThread 的 quit 槽函数,退出事件循环。
    • 当线程结束时,调用 Worker 的 deleteLater 槽函数,删除 Worker 对象。
  • 调用 thread.start() 启动线程,开始执行工作任务。
  • 调用 thread.wait() 在主线程中等待新线程结束。
  • 返回 0,表示程序正常结束。

5. 线程安全

        多线程编程中,线程安全是一个重要问题。QThread 提供了一些机制来帮助实现线程安全,例如 QMutex, QSemaphore, QWaitCondition 等。

#include <QMutex>
#include <QThread>
#include <QDebug>

// 全局互斥锁,用于保护共享资源
QMutex mutex;

// SafeWorker类继承自QObject,用于执行线程中的工作任务
class SafeWorker : public QObject {
    Q_OBJECT

public slots:
    // 槽函数,执行工作任务
    void doWork() {
        // 锁定互斥锁,保护共享资源
        mutex.lock();
        for (int i = 0; i < 5; ++i) {
            qDebug() << "SafeWorker running:" << i;
            QThread::sleep(1); // 线程休眠1秒,模拟耗时操作
        }
        // 解锁互斥锁
        mutex.unlock();
    }
};

int main() {
    // 创建一个QThread对象,用于管理新线程
    QThread thread;

    // 创建一个SafeWorker对象,执行具体的工作任务
    SafeWorker worker;

    // 将SafeWorker对象移到新线程中运行
    worker.moveToThread(&thread);

    // 连接QThread的started信号和SafeWorker的doWork槽
    // 当线程启动时,将调用SafeWorker的doWork槽函数
    QObject::connect(&thread, &QThread::started, &worker, &SafeWorker::doWork);

    // 连接SafeWorker的doWork槽函数和QThread的quit槽
    // 当SafeWorker完成工作后,将调用QThread的quit槽函数,退出线程事件循环
    QObject::connect(&worker, &SafeWorker::doWork, &thread, &QThread::quit);

    // 连接QThread的finished信号和SafeWorker的deleteLater槽
    // 当线程结束时,将调用SafeWorker的deleteLater槽函数,删除SafeWorker对象
    QObject::connect(&thread, &QThread::finished, &worker, &QObject::deleteLater);

    // 启动线程,开始执行工作任务
    thread.start();

    // 在主线程中等待新线程结束
    thread.wait();

    // 返回0,表示程序正常结束
    return 0;
}

全局互斥锁

QMutex mutex;

 创建一个全局的互斥锁 mutex,用于保护共享资源。

SafeWorker 类定义

class SafeWorker : public QObject {
    Q_OBJECT

public slots:
    void doWork() {
        mutex.lock(); // 锁定互斥锁,保护共享资源
        for (int i = 0; i < 5; ++i) {
            qDebug() << "SafeWorker running:" << i;
            QThread::sleep(1); // 线程休眠1秒,模拟耗时操作
        }
        mutex.unlock(); // 解锁互斥锁
    }
};
  • SafeWorker 类继承自 QObject
  • doWork 是一个槽函数,用于执行具体的工作任务。在这里,通过锁定和解锁互斥锁来保护共享资源,确保线程安全。

main 函数

  • 创建一个 QThread 对象 thread,用于管理新线程。
  • 创建一个 SafeWorker 对象 worker,用于执行具体的工作任务。
  • 使用 moveToThread 方法将 SafeWorker 对象移到新线程中运行。
  • 使用 QObject::connect 方法连接信号和槽:
    • 当线程启动时,调用 SafeWorker 的 doWork 槽函数。
    • 当 SafeWorker 完成工作后,调用 QThread 的 quit 槽函数,退出事件循环。
    • 当线程结束时,调用 SafeWorker 的 deleteLater 槽函数,删除 SafeWorker 对象。
  • 调用 thread.start() 启动线程,开始执行工作任务。
  • 调用 thread.wait() 在主线程中等待新线程结束。
  • 返回 0,表示程序正常结束。

6. 实际应用中的重要性

        QThread 在实际应用中非常重要,特别是在需要进行耗时操作的场景下,如网络请求、数据库操作、大文件读写等。使用 QThread 可以显著提高应用程序的响应速度和用户体验。

7. 结论

        QThread 是 Qt 框架中一个强大且灵活的类,能够有效地实现多线程编程。通过本文的介绍,希望读者能够掌握 QThread 的基本概念、使用方法以及在实际应用中的重要性,从而更好地开发高性能的 Qt 应用程序。

8. 参考资料

  • Qt Documentation: QThread
  • Qt Documentation: Signals and Slots
  • Qt Documentation: Thread Support in Qt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2167735.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试标准流程(思维导图版)

一套标准的流程在实际工作落地并执行起来&#xff0c;针对管理可起到很好的作用。 针对效率可在工作中不断的执行&#xff0c;执行后不断的进行优化&#xff0c;再次执行&#xff0c;在不断的工作实践中慢慢完善最终适用于整个团队。 这就是标准流程的作用与实际的好处&#…

实景三维夯实数字乡村孪生底座

随着数字乡村建设的不断推进&#xff0c;实景三维技术在乡村规划、管理、服务等方面发挥着越来越重要的作用。本文将探讨实景三维技术如何夯实数字乡村的孪生底座&#xff0c;为乡村的可持续发展提供强有力的支撑。 一、数字乡村建设的背景 数字乡村建设是推动乡村全面振兴、…

神经网络(一):神经网络入门

文章目录 一、神经网络1.1神经元结构1.2单层神经网络&#xff1a;单层感知机1.3两层神经网络&#xff1a;多层感知机1.4多层神经网络 二、全连接神经网络2.1基本结构2.2激活函数、前向传播、反向传播、损失函数2.2.1激活函数的意义2.2.2前向传播2.2.3损失函数、反向传播2.2.4梯…

【掌桥科研-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

企业源代码一定要加密!10款超级好用的源代码加密软件推荐

在如今竞争激烈的商业环境中&#xff0c;源代码是企业的核心资产之一。对于软件开发公司、技术公司以及以技术驱动的企业来说&#xff0c;保护源代码不被盗窃、泄露或非法篡改至关重要。如果源代码泄露&#xff0c;不仅会对企业的市场竞争力造成巨大打击&#xff0c;还可能导致…

pycirclize python包画circos环形图

pycirclize python包画circos环形图 很多小伙伴都有画环形图的需求&#xff0c;网上也有很多画环形图的教程&#xff0c;讲解circos软件和circlize R包的比较多&#xff0c;本文介绍一款python包:pyCirclize。适合喜欢python且希望更灵活作图的小伙伴。 pyCirclize包实际上也…

衡石分析平台系统管理手册-功能配置之SMTP 服务

SMTP 服务​ SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议。它是一组用于从源地址到目的地址传输邮件的规范&#xff0c;通过它来控制邮件的中转方式。 HENGSHI 用户需要开启 SMTP 服务并进行配置&#xff0c;才能收到系统发送邮件。 SMTP 服务需要用户配置服务器…

基于STM32的智能温室监控系统

目录 引言项目背景环境准备 硬件准备软件安装与配置系统设计 系统架构关键技术代码示例 传感器数据采集自动控制风扇与洒水系统实时数据展示与报警应用场景结论 1. 引言 智能温室监控系统是农业现代化的重要组成部分&#xff0c;能够通过传感器实时监测温度、湿度和光照等环…

20240926给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Buildroot的EVB4方案【通过HDMI0显示】

20240926给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Buildroot的EVB4方案【通过HDMI0显示】 2024/9/26 15:15 1、由于荣品RD-RK3588-AHD开发板没有HDMI1部分&#xff0c;因此拿掉了HDMI1的配置部分&#xff1a; Z:\rk3588s_20230620\kernel\arch\arm64\boot\dts\rockchip\rk358…

嵌入式学习——进程间通信方式(1)——有名管道和匿名管道

一、基本概念 我们要知道管道为什么叫做管道&#xff0c;管道就好比我们生活中的水管&#xff0c;水总是从一端流向另一端&#xff0c;你总不能从水龙头往里灌水吧&#xff0c;它只能出水。管道也是类似的&#xff0c;数据从管子的一端传入&#xff0c;在另一端进行数据的读取…

电脑使用技巧:C盘大文件如何清理?

随着时间的推移&#xff0c;电脑C盘可能会因为各种大文件的堆积而变得容量不足&#xff0c;影响系统运行速度。在这篇文章中&#xff0c;小编将分享几种C盘大文件清理的小妙招&#xff0c;让你的电脑流畅运行。 方法一&#xff1a;使用系统自带的磁盘清理 Windows系统提供了内…

画册翻页电子版是如何制作的?

​随着科技的不断发展&#xff0c;电子出版逐渐成为主流&#xff0c;画册翻页电子版也应运而生。它不仅保留了传统画册的精美风格&#xff0c;还融入了现代电子产品的便捷性。那么&#xff0c;画册翻页电子版究竟是如何制作的呢&#xff1f; 1.要制作电子杂志,首先需要选择一款…

峟思传感器:基坑监测的主要内容与技术

在现代城市建设和土木工程中&#xff0c;基坑监测扮演着至关重要的角色。基坑监测是指在基坑开挖和施工过程中&#xff0c;对基坑及其周边环境进行实时观测和分析的技术手段&#xff0c;以确保工程的安全性和有效性。本文将详细介绍基坑监测的主要内容及其所采用的关键技术。 点…

前端js下载文件时后缀名多出一个下划线(已解决)

问题&#xff1a;前端js下载文件时后缀名多出一个下划线 在打印的时候发现文件名啥啥啥的都没问题&#xff0c;创建的元素似乎也没问题。 但是呢结果&#xff1f;多了个下划线。 原因 细心的你可能发现了a标签的download的内容是双层双引号。具体原因可能是谷歌浏览器做了安全…

解读 Story Protocol:IP 与区块链的潜力与障碍

撰文&#xff1a;100y.eth 编译&#xff1a;J1N&#xff0c;Techub News 8 月&#xff0c;据 The Block 报道&#xff0c;专注于知识产权&#xff08;IP&#xff09;的区块链 Story 宣布完成 a16z Crypto 领投 8000 万美元 B 轮融资&#xff0c;参投方包括 Polychain Capital&…

VBA技术资料MF204:右键多按钮弹出菜单中使用图标

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

MySQL常见面试总结

MySQL基础 什么是关系型数据库&#xff1f; 顾名思义&#xff0c;关系型数据库&#xff08;RDB&#xff0c;Relational Database&#xff09;就是一种建立在关系模型的基础上的数据库。关系模型表明了数据库中所存储的数据之间的联系&#xff08;一对一、一对多、多对多&…

【大数据】元数据是解锁数据价值的关键

在信息爆炸的数字时代&#xff0c;数据无处不在&#xff0c;它以多种形式存在&#xff0c;从文本文档到数字图片&#xff0c;从交易记录到科学测量。然而&#xff0c;如果没有合适的数据管理和理解&#xff0c;这些数据的价值就会大打折扣。如何提高数据价值呢&#xff1f;这就…

IDA Pro基本使用

IDA Pro基本使用 1.DllMain的地址是什么? 打开默认在的位置1000D02E就是DllMain地址 按空格键可以看到图形化界面选择options、general勾选对应的选项在图像化也能看到 2.使用Imports 窗口并浏览到 gethostbyname&#xff0c;导入函数定位到什么地址? 这里可以打开Impo…

2024 Python3.10 系统入门+进阶(十六):正则表达式

目录 一、认识正则表达式二、正则表达式基本语法2.1 行界定符2.2 单词定界符2.3 字符类2.4 选择符2.5 范围符2.6 排除符2.7 限定符2.8 任意字符2.9 转义字符2.10 反斜杠2.11 小括号2.11.1 定义独立单元2.11.2 分组 2.12 反向引用2.13 特殊构造2.14 匹配模式 三、re模块3.1 comp…