【Qt开发】Qt系统(五)-> Qt 多线程

文章目录

  • [1 -> 概述](#1 -> 概述)
    • [1.1 -> 多线程的意义](#1.1 -> 多线程的意义)
    • [1.2 -> Qt 多线程的特点](#1.2 -> Qt 多线程的特点)
  • [2 -> Qt 多线程的核心](#2 -> Qt 多线程的核心)
    • [2.1 -> QThread 概述](#2.1 -> QThread 概述)
    • [2.2 -> 常用API详解](#2.2 -> 常用API详解)
      • [2.2.1 -> run()](#2.2.1 -> run())
      • [2.2.2 -> start()](#2.2.2 -> start())
      • [2.2.3 -> currentThread()](#2.2.3 -> currentThread())
      • [2.2.4 -> isRunning()](#2.2.4 -> isRunning())
      • [2.2.5 -> sleep(), msleep(), usleep()](#2.2.5 -> sleep(), msleep(), usleep())
      • [2.2.6 -> wait()](#2.2.6 -> wait())
      • [2.2.7 -> terminate()](#2.2.7 -> terminate())
      • [2.2.8 -> finished() 信号](#2.2.8 -> finished() 信号)
  • [3 -> 使用QThread构建多线程应用](#3 -> 使用QThread构建多线程应用)
    • [3.1 -> 创建自定义线程类](#3.1 -> 创建自定义线程类)
    • [3.2 -> 线程启动与执行流程](#3.2 -> 线程启动与执行流程)
    • [3.3 -> 线程间通信](#3.3 -> 线程间通信)
    • [3.4 -> 注意事项与最佳实践](#3.4 -> 注意事项与最佳实践)
  • [4 -> 代码示例:倒计时](#4 -> 代码示例:倒计时)
  • [5 -> 总结](#5 -> 总结)

1 -> 概述

在软件开发中,尤其是在图形用户界面(GUI)应用程序中,多线程编程是一种常见且重要的技术手段。Qt作为一个成熟的跨平台C++框架,提供了强大而友好的多线程支持,使得开发者能够轻松构建高效、响应迅速且功能强大的应用程序。

1.1 -> 多线程的意义

传统单线程程序在执行耗时操作时,往往会阻塞主线程,导致用户界面无响应,给用户带来不良体验。多线程允许我们将耗时操作(如网络请求、文件读写、复杂计算、数据解析等)移至后台线程执行,而主线程则专注于UI更新和用户交互,从而保持界面的流畅性和响应性。

1.2 -> Qt 多线程的特点

Qt对多线程的支持不仅封装了底层系统API(如pthread或Windows线程API),还引入了信号槽机制与线程安全的对象通信方式,极大地简化了多线程编程的复杂度。此外,Qt的多线程模型与Qt的事件循环(Event Loop)和对象树(Object Tree)深度集成,为开发者提供了统一的编程体验。

2 -> Qt 多线程的核心

2.1 -> QThread 概述

QThread 是Qt中用于表示和控制线程的核心类。每个 QThread 对象代表一个独立的执行线程。它提供了线程生命周期管理、优先级设置、状态查询等功能,是构建多线程应用的基石。

2.2 -> 常用API详解

2.2.1 -> run()

这是线程的入口函数,必须被子类重写。线程启动后,run() 方法中的代码将在新线程中执行。开发者应在此处实现线程的核心逻辑。

2.2.2 -> start()

启动线程的方法。调用 start() 后,线程会进入就绪状态,并最终执行 run() 方法。操作系统会根据设定的优先级调度线程执行。如果线程已经在运行,再次调用 start() 不会有任何效果。

2.2.3 -> currentThread()

静态方法,返回一个指向当前执行线程的 QThread 指针。这在需要识别线程上下文时非常有用。

2.2.4 -> isRunning()

返回线程当前是否正在运行。可用于检查线程状态,以便决定是否启动、停止或等待线程。

2.2.5 -> sleep(), msleep(), usleep()

这些方法用于使当前线程休眠指定时长,单位分别为秒、毫秒和微秒。常用于模拟耗时操作或控制线程执行节奏。

2.2.6 -> wait()

阻塞当前线程,直到目标线程执行完毕或等待超时。该方法与POSIX的 pthread_join() 功能类似,是线程同步的常用手段。

2.2.7 -> terminate()

强制终止线程的执行。由于此方法可能导致资源未释放或状态不一致,应谨慎使用,通常建议通过信号或标志位让线程优雅退出。

2.2.8 -> finished() 信号

在线程执行结束(即 run() 方法返回)时发出。连接此信号可在线程结束时进行资源清理、状态更新等操作。

3 -> 使用QThread构建多线程应用

3.1 -> 创建自定义线程类

使用Qt进行多线程开发,通常需要创建一个继承自 QThread 的自定义类。在这个类中重写 run() 方法,将需要在线程中执行的逻辑放在其中。这种方式清晰地将线程逻辑与主线程分离,便于维护和理解。

3.2 -> 线程启动与执行流程

  1. 实例化自定义线程类,创建线程对象。
  2. 调用 start() 方法启动线程 。注意,不应直接调用 run() 方法,否则代码仍将在调用者线程中执行,失去多线程意义。
  3. 线程开始执行后,run() 方法中的代码将在新线程中运行。
  4. 线程任务完成后,run() 方法返回,线程自动结束,并发出 finished() 信号。

3.3 -> 线程间通信

由于Qt的对象默认不是线程安全的,直接在不同线程间访问对象可能引发问题。Qt推荐使用信号槽机制进行线程间通信:

  • 跨线程信号槽连接:当信号和槽位于不同线程时,Qt会自动将槽的调用转换为事件,放入接收对象所在线程的事件队列中,从而安全地跨线程调用。
  • 使用 QMetaObject::invokeMethod:该方法可在指定线程中调用对象的槽函数或可调用的方法,是另一种安全的跨线程调用方式。

3.4 -> 注意事项与最佳实践

  • UI操作限制:所有对图形界面(如更新标签文本、修改控件状态等)的操作必须在主线程中执行。后台线程应通过信号将结果发送给主线程,由主线程负责UI更新。
  • 连接类型connect() 函数的第五个参数 Qt::ConnectionType 在多线程环境中尤为重要。常用的有:
    • Qt::AutoConnection(默认):自动判断,跨线程时为队列连接。
    • Qt::QueuedConnection:确保槽在接收者线程的事件循环中被调用,是跨线程通信的安全方式。
  • 资源管理 :确保在线程结束时释放其持有的资源,避免内存泄漏。可利用 finished() 信号与 deleteLater() 方法安全清理对象。
  • 线程退出 :尽可能让线程自然退出(即 run() 方法正常返回),避免使用 terminate() 强制终止。

4 -> 代码示例:倒计时

thread.h

cpp 复制代码
#ifndef THREAD_H
#define THREAD_H

#include <QWidget>
#include <QThread>

class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();

// 重要的目的是重写父类的 run 方法.
    void run();

signals:
    void notify();

};

#endif // THREAD_H

widget.h

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "thread.h"

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();



private:
    Ui::Widget *ui;

    Thread thread;

    void handle();
};
#endif // WIDGET_H

thread.cpp

cpp 复制代码
#include "thread.h"


Thread::Thread()
{

}

void Thread::run()
{
    for (int i = 0; i < 10; i++)
    {
        // sleep 本身是 QThread 的成员函数, 就可以直接调用
        sleep(1);

        // 发送一个信号, 通知主线程
        emit notify();
    }
}

widget.cpp

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(&thread, &Thread::notify, this, &Widget::handle);

    thread.start();
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handle()
{

    int value = ui->lcdNumber->intValue();
    value --;

    ui->lcdNumber->display(value);
}

5 -> 总结

Qt的多线程框架为开发者提供了一套高效、安全且易于使用的工具集。通过 QThread 类,我们可以轻松创建和管理线程,将耗时任务移至后台,从而提升应用程序的整体性能和用户体验。信号槽机制与事件循环的结合,使得跨线程通信既安全又简便,极大地降低了多线程编程的复杂性。

在实际开发中,合理规划线程分工、严格遵守UI线程限制、妥善处理线程间同步与通信,是构建健壮多线程应用的关键。随着计算机硬件多核心的普及,掌握Qt多线程编程已成为现代GUI开发者不可或缺的技能之一。

多线程编程虽然强大,但也会引入诸如竞争条件、死锁、资源管理等挑战。因此,在深入使用前,应该充分理解线程原理,并结合Qt文档和最佳实践,逐步构建稳定高效的多线程应用程序。


感谢各位大佬支持!!!

互三啦!!!

相关推荐
Larry_Yanan9 小时前
Qt多进程(九)命名管道FIFO
开发语言·c++·qt·学习·ui
聆风吟º9 小时前
【C++藏宝阁】C++入门:命名空间(namespace)详解
开发语言·c++·namespace·命名空间
优雅的潮叭9 小时前
c++ 学习笔记之 模板元编程
c++·笔记·学习
潇潇云起9 小时前
mapdb
java·开发语言·数据结构·db
飞鹰519 小时前
CUDA入门:从Hello World到矩阵运算 - Week 1学习总结
c++·人工智能·性能优化·ai编程·gpu算力
prettyxian9 小时前
【QT】信号与槽基础:手动连接的原理与实践
开发语言·qt
傻乐u兔9 小时前
C语言初阶————结构体
c语言·开发语言
weixin_445054729 小时前
力扣热题52
开发语言·python
逑之9 小时前
C语言笔记2:C语言数据类型和变量
c语言·开发语言·笔记