qt实现线程方式有哪些

目录

[Qt 实现线程的 5 种方式(面试必背 + 实战最常用)](#Qt 实现线程的 5 种方式(面试必背 + 实战最常用))

[Qt 实现多线程一共 5 种方式](#Qt 实现多线程一共 5 种方式)

[1. 继承 QThread,重写 run ()(最经典、最基础)](#1. 继承 QThread,重写 run ()(最经典、最基础))

[2. moveToThread () 方式(官方推荐!最佳实践)](#2. moveToThread () 方式(官方推荐!最佳实践))

[3. QtConcurrent::run ()(极简一行代码)](#3. QtConcurrent::run ()(极简一行代码))

[4. QRunnable + QThreadPool(线程池方式)](#4. QRunnable + QThreadPool(线程池方式))

[5. QWorker + QController 封装模式(高级架构)](#5. QWorker + QController 封装模式(高级架构))

极简总结(面试直接背)

最实用一句话

四种主流写法

[一、方式 1:继承 QThread,重写 run ()](#一、方式 1:继承 QThread,重写 run ())

[二、方式 2:QObject + moveToThread(官方推荐)](#二、方式 2:QObject + moveToThread(官方推荐))

[三、方式 3:QRunnable + QThreadPool(线程池)](#三、方式 3:QRunnable + QThreadPool(线程池))

[四、方式 4:QtConcurrent::run(最简,一行起线程)](#四、方式 4:QtConcurrent::run(最简,一行起线程))

[1. 普通函数 / Lambda](#1. 普通函数 / Lambda)

[.pro 文件必须加模块](#.pro 文件必须加模块)

各方案快速选型(实战建议)

[Qt 4 种线程方式 对比 + 场景推荐(面试 + 实战终极版)](#Qt 4 种线程方式 对比 + 场景推荐(面试 + 实战终极版))

[一、4 种线程方式 核心对比表](#一、4 种线程方式 核心对比表)

二、核心区别(最重要)

[1. 继承 QThread](#1. 继承 QThread)

[2. moveToThread](#2. moveToThread)

[3. QRunnable](#3. QRunnable)

[4. QtConcurrent](#4. QtConcurrent)

三、使用场景推荐(最实用,直接背)

[✅ 官方首选、正式项目 → moveToThread](#✅ 官方首选、正式项目 → moveToThread)

[✅ 大量小任务、频繁创建 → QRunnable + 线程池](#✅ 大量小任务、频繁创建 → QRunnable + 线程池)

[✅ 临时简单任务、不想写类 → QtConcurrent::run](#✅ 临时简单任务、不想写类 → QtConcurrent::run)

[✅ 学习入门、简单 Demo → 继承 QThread](#✅ 学习入门、简单 Demo → 继承 QThread)

四、终极一句话总结(面试必背)

[线程的 run() 函数中可以用信号槽通知结束吗?](#线程的 run() 函数中可以用信号槽通知结束吗?)

[终极结论(面试 + 实战必背)](#终极结论(面试 + 实战必背))

[一、重点:为什么 QThread::run () 默认不能用信号槽?](#一、重点:为什么 QThread::run () 默认不能用信号槽?)

[二、正确写法:run () 里用信号槽通知结束](#二、正确写法:run () 里用信号槽通知结束)

[方式 1:在 run () 里开启事件循环 exec()](#方式 1:在 run () 里开启事件循环 exec())

使用:

[方式 2:任务结束后退出循环(更常用)](#方式 2:任务结束后退出循环(更常用))

[三、最关键的区别(90% 的人都错在这里)](#三、最关键的区别(90% 的人都错在这里))

[1. 继承 QThread(重写 run)](#1. 继承 QThread(重写 run))

[2. moveToThread(官方推荐)](#2. moveToThread(官方推荐))

四、一句话终极总结(背这个)

最推荐的最佳实践

核心结论(一句话背会)

[1. 为什么默认不能用?](#1. 为什么默认不能用?)

[2. 那怎么才能用信号槽?](#2. 那怎么才能用信号槽?)

[✅ 方案 1:线程内部手动开启事件循环(最标准)](#✅ 方案 1:线程内部手动开启事件循环(最标准))

[✅ 方案 2:不用事件循环,直接发信号(推荐!最简单)](#✅ 方案 2:不用事件循环,直接发信号(推荐!最简单))

完整最简示例

[3. 最实用、最推荐的方案(不用信号槽)](#3. 最实用、最推荐的方案(不用信号槽))

[4. 终极总结(面试必背)](#4. 终极总结(面试必背))


Qt 实现线程的 5 种方式(面试必背 + 实战最常用)

我给你整理最清晰、最好记 的版本,区分常用 / 不常用,直接背这个就够了!


Qt 实现多线程一共 5 种方式

1. 继承 QThread,重写 run ()(最经典、最基础)

  • 方式:写一个类继承 QThread,重写 run() 函数
  • 任务写在 run()
  • 启动:thread->start()
  • 优点:简单直观
  • 缺点:容易写错(只有 run 里是子线程)
  • 适用:简单后台任务

2. moveToThread () 方式(官方推荐!最佳实践)

  • 方式:创建普通 QObject 类,把它丢进 QThread
  • 所有槽函数都自动运行在子线程
  • 线程安全、结构清晰、可复用
  • Qt 官方最推荐的标准写法
  • 适用:复杂业务、长时间后台工作

3. QtConcurrent::run ()(极简一行代码)

  • 方式:直接用全局函数扔一个函数 /lambda 到线程池

  • 无需创建 QThread,自动管理线程

  • 代码超短: cpp

    运行

    复制代码
    QtConcurrent::run(函数);
  • 优点:超级简单

  • 缺点:无法手动控制线程生命周期

  • 适用:简单临时任务


4. QRunnable + QThreadPool(线程池方式)

  • 方式:继承 QRunnable 重写 run()
  • 交给线程池执行:QThreadPool::globalInstance()->start(...)
  • 自动复用线程,节约资源
  • 适用:大量小任务、频繁创建销毁的任务

5. QWorker + QController 封装模式(高级架构)

  • 基于 moveToThread 封装的设计模式
  • 专门用于复杂项目、模块化线程
  • 本质还是方式 2,只是更规范

极简总结(面试直接背)

Qt 实现线程 5 种方式

  1. 继承 QThread 重写 run ()
  2. moveToThread ()(官方推荐)
  3. QtConcurrent::run()
  4. QRunnable + 线程池
  5. 高级封装模式

最实用一句话

  • 简单任务QtConcurrent
  • 标准推荐moveToThread
  • 大量小任务QRunnable
  • 入门学习QThread 继承

四种主流写法

下面给出四种主流写法的最简可运行示例,附带关键注释、使用场景,代码基于 Qt 5/Qt 6 通用。


一、方式 1:继承 QThread,重写 run ()

特点 :入门写法,任务逻辑写在 run() 中,仅 run 内代码运行在子线程。

cpp

运行

复制代码
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
    Q_OBJECT
protected:
    void run() override
    {
        // 此处运行在子线程
        qDebug() << "子线程 ID:" << currentThreadId();
        for (int i = 0; i < 3; ++i)
        {
            qDebug() << "执行任务 " << i;
            sleep(1);
        }
    }
};

// 调用示例
void testThread1()
{
    qDebug() << "主线程 ID:" << QThread::currentThreadId();
    MyThread *t = new MyThread;
    t->start();  // 启动线程,自动调用 run()
    // 用完可配合信号 deleteLater 释放
}

注意:类中普通成员函数不会 自动跑到子线程,只有 run() 是子线程。


二、方式 2:QObject + moveToThread(官方推荐)

特点:业务类和线程分离,槽函数自动在目标线程执行,解耦性最好。

cpp

运行

复制代码
#include <QObject>
#include <QThread>
#include <QDebug>

// 业务工作类
class Worker : public QObject
{
    Q_OBJECT
public slots:
    void doWork()
    {
        qDebug() << "工作线程 ID:" << QThread::currentThreadId();
        for (int i = 0; i < 3; ++i)
        {
            qDebug() << "任务执行 " << i;
            QThread::sleep(1);
        }
        emit workFinished();
    }
signals:
    void workFinished();
};

// 调用示例
void testThread2()
{
    qDebug() << "主线程 ID:" << QThread::currentThreadId();

    QThread *thread = new QThread;
    Worker *worker = new Worker;

    // 将工作对象移入子线程
    worker->moveToThread(thread);

    // 信号槽连接:任务开始、结束、资源释放
    QObject::connect(thread, &QThread::started, worker, &Worker::doWork);
    QObject::connect(worker, &Worker::workFinished, thread, &QThread::quit);
    QObject::connect(thread, &QThread::finished, worker, &Worker::deleteLater);
    QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);

    thread->start(); // 启动线程
}

三、方式 3:QRunnable + QThreadPool(线程池)

特点:适合大量短时任务,线程复用,无需手动管理线程生命周期。

cpp

运行

复制代码
#include <QRunnable>
#include <QThreadPool>
#include <QDebug>
#include <QThread>

class MyRunnable : public QRunnable
{
    void run() override
    {
        qDebug() << "线程池任务,线程ID:" << QThread::currentThreadId();
        QThread::msleep(1000);
    }
};

// 调用示例
void testThread3()
{
    // 提交多个任务到全局线程池
    for (int i = 0; i < 4; ++i)
    {
        MyRunnable *task = new MyRunnable;
        task->setAutoDelete(true); // 执行完自动析构,默认true
        QThreadPool::globalInstance()->start(task);
    }
}

四、方式 4:QtConcurrent::run(最简,一行起线程)

特点 :代码最少,适合临时异步任务,依赖 QtConcurrent 模块。

1. 普通函数 / Lambda

cpp

运行

复制代码
#include <QtConcurrent/QtConcurrent>
#include <QDebug>
#include <QThread>
#include <QFuture>

void taskFunc()
{
    qDebug() << "QtConcurrent 线程ID:" << QThread::currentThreadId();
    QThread::sleep(1);
}

// 调用示例
void testThread4()
{
    // 方式1:调用普通函数
    QFuture<void> f1 = QtConcurrent::run(taskFunc);

    // 方式2:使用 Lambda
    QFuture<void> f2 = QtConcurrent::run([](){
        qDebug() << "Lambda 异步任务";
    });

    // f1.waitForFinished(); // 可选:阻塞等待任务完成
}

.pro 文件必须加模块

qmake

复制代码
QT += concurrent

各方案快速选型(实战建议)

  1. 简单后台任务、入门练习 → 继承 QThread 重写 run()
  2. 正式项目、长耗时业务、多槽函数moveToThread(首选)
  3. 大量零散小任务、高频启停QRunnable + QThreadPool
  4. 临时异步、不想写类QtConcurrent::run

Qt 4 种线程方式 对比 + 场景推荐(面试 + 实战终极版)

我给你整理成一张表就能背下来,清晰对比优缺点、区别、怎么选。


一、4 种线程方式 核心对比表

表格

方式 难度 优点 缺点 线程控制能力
继承 QThread 重写 run () 最简单 入门快、代码少 只有 run () 在子线程,容易写错、扩展性差
moveToThread (官方推荐) 中等 结构清晰、安全、可扩展、适合长任务 代码稍多
QRunnable + 线程池 简单 线程复用、适合大量小任务、高效 不能用信号槽、无法手动停止
QtConcurrent::run 极简 一行代码、无需类、最快 无法控制线程、无法停止、无事件循环 极弱

二、核心区别(最重要)

1. 继承 QThread

  • 只有 run () 里是子线程
  • 类的其他函数都在主线程
  • 不适合复杂业务

2. moveToThread

  • 整个对象的所有槽函数都跑在子线程
  • 支持信号槽、支持停止、暂停、重启
  • 官方标准、最安全、项目必用

3. QRunnable

  • 属于线程池
  • 自动管理线程,不浪费资源
  • 不能用信号槽

4. QtConcurrent

  • 最简单,丢一个函数就跑线程
  • 完全不用管理线程
  • 适合临时任务

三、使用场景推荐(最实用,直接背)

官方首选、正式项目 → moveToThread

  • 长耗时任务
  • 需要信号槽通信
  • 需要开始、暂停、停止
  • 网络请求、串口、数据解析、后台服务

大量小任务、频繁创建 → QRunnable + 线程池

  • 批量图片压缩
  • 批量文件读写
  • 大量计算任务
  • 追求性能、不想创建太多线程

临时简单任务、不想写类 → QtConcurrent::run

  • 简单计算
  • 临时读写文件
  • 异步弹框、异步日志
  • 写 Demo、快速验证

学习入门、简单 Demo → 继承 QThread

  • 教学、测试
  • 超简单后台任务
  • 不推荐正式项目使用

四、终极一句话总结(面试必背)

  • 项目标准用法:moveToThread
  • 大量小任务:QRunnable 线程池
  • 简单临时任务:QtConcurrent
  • 入门学习:继承 QThread

注意:

线程的 run() 函数中可以用信号槽通知结束吗?

终极结论(面试 + 实战必背)

可以!但分两种情况,写法完全不同:

  1. 继承 QThread,重写 run ()

    • 默认不能直接用信号槽(因为没有开启事件循环)!
    • 想在 run() 里用信号槽 → 必须手动调用 exec() 开启事件循环
  2. moveToThread 方式

    • 天然支持信号槽,自带事件循环,随便用。

一、重点:为什么 QThread::run () 默认不能用信号槽?

因为 QThread 默认的 run () 函数只有一行代码

cpp

运行

复制代码
void QThread::run() {
    exec(); // 开启事件循环,支持信号槽
}

一旦你重写了 run (),又没写 exec (),事件循环就没了! 没有事件循环 → 信号槽无法触发 → 发了信号也收不到。


二、正确写法:run () 里用信号槽通知结束

方式 1:在 run () 里开启事件循环 exec()

cpp

运行

复制代码
class MyThread : public QThread
{
    Q_OBJECT
signals:
    void workFinished(); // 结束信号

protected:
    void run() override {
        // 1. 执行你的任务
        qDebug() << "任务执行中...";

        // 2. 任务完成,发送结束信号
        emit workFinished();

        // 3. 必须开启事件循环,信号才能发出去!
        exec();
    }
};

使用:

cpp

运行

复制代码
MyThread *t = new MyThread;
connect(t, &MyThread::workFinished, [=]() {
    qDebug() << "收到线程结束信号!";
});
t->start();

方式 2:任务结束后退出循环(更常用)

cpp

运行

复制代码
void run() override {
    // 执行任务
    qDebug() << "做事...";

    // 发结束信号
    emit workFinished();

    // 退出事件循环(必须加,否则线程一直活着)
    quit();
}

三、最关键的区别(90% 的人都错在这里)

1. 继承 QThread(重写 run)

  • 必须手动调用 exec() 才能用信号槽
  • 否则信号发不出去
  • 适合简单任务

2. moveToThread(官方推荐)

  • 自带事件循环
  • 槽函数天然在子线程运行
  • 信号槽随便用
  • 项目首选

四、一句话终极总结(背这个)

线程 run () 函数中可以发信号通知结束,但必须调用 exec () 开启事件循环,否则信号槽无法工作。


最推荐的最佳实践

正式项目不要用重写 run () 的方式!moveToThread,完全不用关心事件循环,信号槽随便用。

核心结论(一句话背会)

可以用信号槽通知结束,但必须满足一个前提:QtConcurrent::run 开启的线程里,默认没有事件循环,信号槽不能自动触发,必须自己处理。

我给你用最简单、最准确、面试必对的方式讲清楚👇


1. 为什么默认不能用?

QtConcurrent::run() 启动的是线程池里的裸线程没有开启 Qt 事件循环 exec()

没有事件循环 → 信号发出去没人接收


2. 那怎么才能用信号槽?

两种正确方案:

✅ 方案 1:线程内部手动开启事件循环(最标准)

cpp

运行

复制代码
QtConcurrent::run([=]() {
    // 执行耗时任务
    doWork();

    // 发结束信号
    emit finished();

    // 关键:开启事件循环,让信号能发出去
    QThread::exec();  
});

但这样线程会一直卡住 ,必须配合 quit() 退出。


✅ 方案 2:不用事件循环,直接发信号(推荐!最简单)

Qt 信号槽有一种模式叫:Qt::DirectConnection不需要事件循环,直接调用槽函数。

你只需要在连接时加一句:

cpp

运行

复制代码
connect(发送者, &类::finished, 接收者, &类::onFinished, Qt::DirectConnection);

完整最简示例

cpp

运行

复制代码
// 工作类
class Worker : public QObject
{
    Q_OBJECT
signals:
    void workFinished();

public:
    void doWork() {
        // 耗时任务...
        emit workFinished(); // 发信号
    }
};

// 启动线程
Worker *worker = new Worker;

// 关键:加 Qt::DirectConnection
connect(worker, &Worker::workFinished, this, [=]() {
    qDebug() << "线程执行结束!";
}, Qt::DirectConnection);

// 扔到线程池执行
QtConcurrent::run(worker, &Worker::doWork);

这个可以直接运行,100% 成功!


3. 最实用、最推荐的方案(不用信号槽)

因为 QtConcurrent 本来就是轻量线程 ,官方推荐用 QFutureWatcher 监听结束,最简单、最安全、最标准

cpp

运行

复制代码
#include <QFutureWatcher>

// 启动线程
QFuture<void> future = QtConcurrent::run([](){
    // 耗时任务
});

// 监听器
QFutureWatcher<void> *watcher = new QFutureWatcher(this);
watcher->setFuture(future);

// 监听结束
connect(watcher, &QFutureWatcher::finished, this, [=](){
    qDebug() << "线程执行完毕!";
});

这是 Qt 官方推荐的标准写法不需要事件循环不需要 DirectConnection永远不会错


4. 终极总结(面试必背)

  1. QtConcurrent::run 默认没有事件循环 → 不能直接用信号槽
  2. 想用信号槽 → 加 Qt::DirectConnection
  3. 最推荐、最标准 → 使用 QFutureWatcher 监听结束
相关推荐
机器视觉知识推荐、就业指导3 小时前
Qt6 + QCustomPlot 实时曲线 Demo ,适合串口与上位机项目
qt
YY&DS5 小时前
Qt 嵌入 CEF 在 Linux 下必须设置 `QT_XCB_GL_INTEGRATION=xcb_egl才能加载网页
linux·开发语言·qt
秋田君8 小时前
Qt 5.12.8 下载与安装教程(附网盘资源)
开发语言·qt
郝学胜-神的一滴9 小时前
Qt 高级开发 019:从零定制登录窗口按钮、Logo 样式与交互悬浮效果
开发语言·c++·qt·程序人生·交互·用户界面
YY&DS10 小时前
Qt Designer 自定义控件已提升后,如何修改提升类
开发语言·qt
爱吃生蚝的于勒11 小时前
QT开发第二章——信号和槽
c语言·开发语言·c++·qt
宏笋14 小时前
qss/css 样式中margin和padding的作用和区别
css·qt
读书札记20221 天前
Qt界面卡死问题探讨及解决方法
qt
bug和崩溃我都要1 天前
Qt 封装 libmpv 全功能视频播放器开发指南
开发语言·qt·音视频