C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > QT 多线程QThread

QT中多线程QThread基础的使用小结

作者:艾莉丝努力练剑

本文主要介绍了QT中多线程QThread基础,从继承run()到信号槽跨线程通信,手把手教你实现倒计时案例,感兴趣的可以了解一下

1 ~> Qt 多线程 QThread 基础总览

1.1 Qt 多线程与系统线程的关系

**注意:**掌握操作系统原生线程原理,是理解上层std::thread、QThread的前置基础。

1.2 QThread 设计思路:继承重写 run ()

1.2.1 线程入口函数

1.3 QThread 核心 API

API功能说明
virtual void run()线程入口虚函数,子类重写,线程业务逻辑编写位置,禁止手动调用
void start(Priority = InheritPriority)向操作系统发起创建线程请求;线程已运行时调用无效果;线程就绪后自动执行run()
static QThread* currentThread()静态方法,返回当前正在执行代码所属线程的QThread对象指针
bool isRunning() const判断内核线程是否处于运行状态,run()未执行完毕返回 true
sleep(unsigned long secs)线程休眠,单位秒;仅作用于调用该函数的线程
msleep(unsigned long msecs)线程休眠,单位毫秒
usleep(unsigned long usecs)线程休眠,单位微秒
bool wait(unsigned long time = ULONG_MAX)阻塞等待线程结束,等价 Linuxpthread_join();等待线程执行完毕或者超时返回;线程未启动直接返回 true;超时返回 false;永久阻塞使用默认参数
void terminate()强制终止线程,操作系统调度时机不确定,资源无法安全释放,业务开发禁止使用
void finished()信号线程执行完毕发射该信号,可绑定槽做线程资源清理工作

重点:sleep/msleep/usleep是线程休眠,会阻塞当前调用线程;GUI 主线程禁止调用休眠,会冻结界面事件循环。

2 ~> QThread 继承方式实战:多线程倒计时案例

2.1 核心约束:Qt GUI 线程规则

2.2 项目实现步骤

  1. 创建 Qt Widget 项目,UI 界面放置QLCDNumber数字显示控件。
  2. 创建 C++ 类Thread,继承QThread,启用Q_OBJECT宏。
  3. 子类重写run()作为线程入口;定义自定义信号用于向主线程通知计时事件。
  4. Widget 主线程创建 Thread 对象;connect 绑定信号槽;调用start()启动子线程。
  5. 子线程 run 循环计时休眠,通过 emit 发射信号;主线程槽函数接收信号更新 LCD 显示。

2.2.1 thread.h(线程子类头文件)

#ifndef THREAD_H
#define THREAD_H

#include <QThread>

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

protected:
    //重写线程入口函数run,protected虚函数
    void run() override;

signals:
    //自定义信号,无需实现,MOC工具自动生成实现
    void notify();
};

#endif // THREAD_H

2.2.2 thread.cpp(线程子类实现)

#include "thread.h"

Thread::Thread()
{

}

void Thread::run()
{
    //子线程执行逻辑,禁止操作UI控件
    for(int i = 0; i < 10; ++i)
    {
        //休眠1秒,阻塞当前子线程,不会影响GUI主线程
        sleep(1);
        //发射自定义信号,通知主线程更新界面
        emit notify();
    }
}

2.2.3 widget.h(主窗口头文件)

#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 slots:
    //槽函数:接收子线程信号,更新UI
    void handle();

private:
    Ui::Widget *ui;
    Thread thread; //子线程对象
};

#endif // WIDGET_H

2.2.4 widget.cpp(主窗口实现)

#include "widget.h"
#include "ui_widget.h"

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

    //绑定子线程notify信号到主线程handle槽函数
    //跨线程自动使用QueuedConnection
    connect(&thread, &Thread::notify, this, &Widget::handle);

    //启动子线程,调用操作系统API创建内核线程,自动执行run()
    thread.start();
}

Widget::~Widget()
{
    //析构前确保线程执行完毕,防止对象销毁线程还在运行
    thread.wait();
    delete ui;
}

void Widget::handle()
{
    //槽函数运行在主线程,可以安全操作UI控件
    int value = ui->lcdNumber->intValue();
    value--;
    ui->lcdNumber->display(value);
}

2.3 程序时序流程

  1. Widget 构造函数执行,thread.start()调用,操作系统创建子线程。
  2. 子线程调度执行Thread::run(),循环执行sleep(1),每 1 秒emit notify()发射信号。
  3. 信号跨线程投递,放入主线程事件队列。
  4. 主线程事件循环取出事件,执行handle()槽函数,更新 LCD 数字。
  5. for 循环执行 10 次结束,run()函数 return;线程结束,发射finished()信号。

3 ~> Qt 客户端多线程应用场景

3.1 服务器多线程 vs Qt 客户端多线程目标差异

3.2 客户端阻塞根源

GUI 主线程职责:运行事件循环,处理鼠标、键盘、窗口绘制全部用户交互。

3.3 正确处理方案

3.4 典型业务场景

  1. 网络:大文件上传下载、网络请求等待响应。
  2. IO:大批量磁盘文件读写、加载大型资源文件。
  3. 后台计算:耗时数据处理,不允许卡住界面。

4 ~> 重要补充知识点

4.1 对象依附线程(Thread Affinity)

4.2 QThread 两种主流用法

  1. 继承重写run()(本笔记示例用法),适合业务逻辑完整、循环常驻运行的任务。
  2. QObject + moveToThread():不继承 QThread,将工作对象移动到 QThread,适合使用信号槽驱动、存在事件循环的任务(Qt 官方更推荐)。

4.3 线程退出注意事项

  1. 禁止调用terminate()强制杀死线程,资源泄漏风险。
  2. 循环任务线程,推荐设置原子 bool 标记位,控制 run 循环正常退出,外部调用wait()等待线程安全结束。
  3. 对象析构前,务必保证线程已经结束,避免对象销毁,线程还在访问成员造成崩溃。

4.4 信号槽连接类型

结尾

到此这篇关于QT中多线程QThread基础的文章就介绍到这了,更多相关QT 多线程QThread内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文