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 是并发任务工具

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

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

相关推荐
2301_790300963 小时前
Python单元测试(unittest)实战指南
jvm·数据库·python
zhuqiyua3 小时前
第一次课程家庭作业
c++
只是懒得想了3 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
九章-3 小时前
一库平替,融合致胜:国产数据库的“统型”范式革命
数据库·融合数据库
m0_736919103 小时前
模板编译期图算法
开发语言·c++·算法
玖釉-3 小时前
深入浅出:渲染管线中的抗锯齿技术全景解析
c++·windows·图形渲染
【心态好不摆烂】3 小时前
C++入门基础:从 “这是啥?” 到 “好像有点懂了”
开发语言·c++
dyyx1113 小时前
基于C++的操作系统开发
开发语言·c++·算法
AutumnorLiuu3 小时前
C++并发编程学习(一)——线程基础
开发语言·c++·学习
m0_736919103 小时前
C++安全编程指南
开发语言·c++·算法