一、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 是并发任务工具
没有"谁更高级",只有"谁更合适"
正确选型,代码会更稳定、可维护