QThread 与 QtConcurrent

一、Qt 为什么要提供两套并发机制?

Qt 的目标并不是让你"直接操作线程",而是:

保证 UI 响应的同时,安全地执行后台任务

因此 Qt 把并发明确分成两类:

类型 工具 设计目标
控制型并发 QThread 长生命周期、可控线程
任务型并发 QtConcurrent 一次性并行任务

二、QThread ------ 控制型线程(长期运行任务)

2.1 一个非常重要的认知

QThread 不是"线程函数",而是"线程容器"

这也是 Qt 官方一直强调的一点:

不推荐把业务逻辑写在 QThread::run()里

推荐使用 QObject + moveToThread()

2.2 错误示例(仍然非常常见)

复制代码
class MyThread : public QThread {
    void run() override {
        while (true) {
            doWork();
        }
    }
};

问题包括但不限于:

  • 无法安全使用信号槽

  • 线程和业务逻辑强耦合

  • 难以优雅停止

  • 容易导致资源泄漏

2.3 官方推荐写法:Worker + moveToThread

(1)定义 Worker 对象
复制代码
class Worker : public QObject {
    Q_OBJECT
public slots:
    void doWork() {
        qDebug() << "Worker running in" << QThread::currentThread();
        while (m_running) {
            QThread::sleep(1);
            qDebug() << "working...";
        }
        emit finished();   // ⭐ 关键:工作自然结束时发射
    }
    void stop() {
        m_running = false;
    }
signals:
    void finished();
private:
    std::atomic_bool m_running{true};
};
(2)创建线程并建立关系
复制代码
QThread* thread = new QThread;
Worker* worker = new Worker;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::finished, thread, &QThread::quit);
connect(worker, &Worker::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();

2.4 为什么一定要自己 emit finished()?

这是很多人容易忽略的一点。

Qt 不会帮你判断"工作是否完成"

  • QThread::finished → 线程事件循环结束

  • Worker::finished → 业务逻辑结束

只有 Worker 自己最清楚 "什么时候算干完了"


2.5 QThread 的典型使用场景

非常适合:

串口通信

Socket / TCP 服务

硬件数据采集(ECG / 传感器)

后台服务、心跳检测

不适合:

一次性计算

简单函数并行

2.6 QThread 常见坑

错误 后果
在 UI 线程调用 wait() UI 卡死
使用 terminate() 随机崩溃
忘记 moveToThread() 实际仍跑在 UI 线程
直接 delete Worker 跨线程析构,极不安全

三、QtConcurrent ------ 任务型并发(简单高效)

3.1 QtConcurrent 的设计理念

你只关心"任务"和"结果",不关心线程本身

特点:

基于线程池

自动调度

非常适合 CPU 密集型任务

3.2 最简单用法

复制代码
QtConcurrent::run([]{
    qDebug() << "run in" << QThread::currentThread();
});

3.3 带返回值的任务

复制代码
QFuture<int> future = QtConcurrent::run([]{
    QThread::sleep(2);
    return 42;
});

3.4 使用 QFutureWatcher 获取结果(推荐)

复制代码
QFutureWatcher<int>* watcher = new QFutureWatcher<int>(this);
connect(watcher, &QFutureWatcher<int>::finished, this, [=] {
    qDebug() << "result =" << watcher->result();
    watcher->deleteLater();
});
watcher->setFuture(future);

3.5 并行 map / reduce(非常实用)

并行处理数据
复制代码
QVector<int> data{1, 2, 3, 4, 5};
QtConcurrent::map(data, [](int& v) {
    v *= 2;
});

含义是:对 data 容器里的每一个元素,使用多个线程并行执行 v *= 2,直接修改原容器中的值。

复制代码
data = {2, 4, 6, 8, 10}

并行统计

复制代码
int sum = QtConcurrent::blockingMappedReduced(
    data,
    [](int v) { return v; },
    [](int& result, int v) { result += v; }
);

[](int v) { return v; }

含义是:对 data 中的每一个元素,执行一次这个函数

复制代码
1 → 12 → 23 → 3...

[](int& result, int v) { result += v; }

含义是:把 map 的结果逐个累加到 result 里​​​​​​​

复制代码
result = 0result += 1result += 2result += 3result += 4

总的等价路程:​​​​​​​

复制代码
并行执行:map(1) → 1map(2) → 2map(3) → 3map(4) → 4
然后合并:result = 0result += 1result += 2result += 3result += 4
最终:sum = 10

3.6 QtConcurrent 的典型场景

非常适合:

算法计算(FFT / 滤波)

ECG 数据分析

文件解析(CSV / bin)

图像处理

不适合:

while(true) 循环

长时间监听

串口 / Socket

四、QThread vs QtConcurrent 一张表看懂

对比项 QThread QtConcurrent
生命周期控制
是否支持循环
使用复杂度
信号槽 原生支持 需 Watcher
硬件采集
算法计算 ⚠️

五、选型口诀(经验总结)

要控制,用 QThread
要计算,用 QtConcurrent
要长期,用 QThread
要简单,用 QtConcurrent

六、总结

QThread 是线程管理工具

QtConcurrent 是并发任务工具

没有"谁更高级",只有"谁更合适"

正确选型,代码会更稳定、可维护

相关推荐
小龙报1 天前
【51单片机】不止是调光!51 单片机 PWM 实战:呼吸灯 + 直流电机正反转 + 转速控制
数据结构·c++·stm32·单片机·嵌入式硬件·物联网·51单片机
彩妙不是菜喵1 天前
C++:深入浅出讲解=>多态
开发语言·c++
xcLeigh1 天前
基于 IoT-benchmark 的时序数据库性能测试实战:从安装到结果分析
数据库·物联网·性能测试·时序数据库·iotdb
SmartBrain1 天前
FastAPI实战:基于 SQLAlchemy的后端接口开发流程
数据库·架构·fastapi
@@神农1 天前
PostgreSQL- SQL语句的执行过程(二)
数据库·sql·postgresql
_codemonster1 天前
MySQL驱动(8.x版本)和MySQL驱动(5.x版本)区别
数据库·mysql
lightqjx1 天前
【C++】C++11 - Lambda表达式+包装器
开发语言·c++·c++11·lambda·包装器
BHXDML1 天前
操作系统实验:(七)动态分区分配方式的模拟
开发语言·数据库·操作系统
LaughingZhu1 天前
Product Hunt 每日热榜 | 2026-02-19
数据库·人工智能·经验分享·神经网络·chatgpt
秋氘渔1 天前
Django事务机制详解:确保数据一致性
数据库