在开发中不可避免的会遇到耗时操作、后台计算、异步任务等需求。此时,使用多线程编程便成为解决性能与响应式 UI 的关键手段。而 Qt 提供了两种主流的并发机制:
QThread
:底层线程控制类,功能强大,适合复杂线程模型。
QtConcurrent
:基于线程池的高级接口,简洁易用,适合并发任务调度。
虽然它们都可实现多线程任务,但各自适用的场景、易用性、控制粒度却有很大不同:
一、概览对比
项目 | QThread | QtConcurrent |
---|---|---|
所属模块 | QtCore |
QtConcurrent |
控制粒度 | 低层级(线程生命周期) | 高层级(任务并发) |
线程管理 | 手动控制(start/exit) | 自动管理(线程池调度) |
使用方式 | 继承/组合/moveToThread() |
使用 QtConcurrent::run() 启动任务 |
支持信号槽 | ✅(需要事件循环) | ❌(任务线程中默认无事件循环) |
可取消任务 | ✅ | ✅(需手动控制) |
易用性 | 复杂 | 简单 |
常见用途 | 长期驻留线程、线程间通信 | 并发处理、异步操作、批量计算 |
二、QThread:强控制、适合长期任务
方式一:继承 QThread
class MyThread : public QThread {
protected:
void run() override {
// 自定义线程逻辑
}
};
方式二:组合 QObject + moveToThread
Worker *worker = new Worker;
QThread *thread = new QThread;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &Worker::doWork);
thread->start();
特点
需要自己控制线程生命周期:
start()
、quit()
、wait()
。适合 UI 与线程交互(信号槽机制)。
可以运行事件循环,处理定时器、socket、事件等。
适合需要持久运行或复杂状态管理的后台任务。
三、QtConcurrent:自动调度,适合短期任务
最常用的方式:QtConcurrent::run()
QtConcurrent::run([](){
doHeavyWork();
});
支持绑定返回值和观察器
QFuture<QString> future = QtConcurrent::run([]() {
return QString("Hello from thread");
});
QFutureWatcher<QString> *watcher = new QFutureWatcher<QString>();
QObject::connect(watcher, &QFutureWatcher<QString>::finished, [=](){
qDebug() << "Result:" << future.result();
watcher->deleteLater();
});
watcher->setFuture(future);
特点
自动使用
QThreadPool
管理线程;任务无需手动创建线程或事件循环;
适合“函数级别”的并发调用;
不适合需要线程交互或长期保持任务状态的场景。
四、完整示例
4.1、非阻塞常见使用(推荐)
QFuture<QString> future = QtConcurrent::run([]() {
QThread::sleep(2);
return QString("任务完成");
});
QFutureWatcher<QString>* watcher = new QFutureWatcher<QString>();
QObject::connect(watcher, &QFutureWatcher<QString>::finished, [=]() {
QString result = future.result(); // ✅ 此时不会阻塞,因为已经 finished
qDebug() << "结果是:" << result;
watcher->deleteLater();
});
watcher->setFuture(future);
4.2、非阻塞带返回值示例
既可以获取到返回值还可以防止任务重复运行
QFuture<int> future;
QFutureWatcher<int> watcher;
void MyWorker::startTask()
{
if (future.isRunning())
return;
future = QtConcurrent::run([]() -> int {
QThread::sleep(2);
return 42;
});
connect(&watcher, &QFutureWatcher<int>::finished, this, [=]() {
qDebug() << "返回值为:" << future.result();
});
watcher.setFuture(future);
}
4.3、错误示例——阻塞获取数据
QFuture<QString> future = QtConcurrent::run([]() {
QThread::sleep(5); // 模拟耗时任务
return QString("Done");
});
QString result = future.result(); // ⛔ 阻塞:等待上面的任务执行完毕
QtConcurrent::run(...)
会把 lambda 提交给线程池去执行(异步),主线程继续往下执行,执行到future.result(),
但是任务还没跑完,结果还没出来;Qt 的
QFuture::result()
必须返回值,所以当前线程(比如主线程)就 阻塞等待,等线程池中的任务执行完成,把结果返回;
方法名 | 是否阻塞 | 说明 |
---|---|---|
future.result() |
✅ 会阻塞 | 阻塞直到任务完成并返回结果 |
future.waitForFinished() |
✅ 会阻塞 | 阻塞直到任务结束 |
future.isRunning() |
❌ 不阻塞 | 非阻塞方式判断任务是否仍在运行 |
future.isFinished() |
❌ 不阻塞 | 判断是否已完成 |
future.resultCount() |
❌ 不阻塞 | 并行结果数量(比如 mapped() ) |
QFutureWatcher::finished() 信号 |
❌ 不阻塞 | 信号通知,推荐 UI 中使用 |
五、建议
不要滥用 QThread:不推荐直接在主线程中执行
run()
;也不建议频繁创建/销毁线程。短期任务用 QtConcurrent 更自然:能显著减少代码量,提高可读性。
任务有 UI 回调?用 QFutureWatcher:避免在
QtConcurrent
的线程中直接访问 UI(线程不安全)。需要信号槽?考虑 moveToThread 模式:结合事件循环,线程间通信更强大。
并行计算/算法任务?可用 QtConcurrent::mapped/filter/reduce:实现更复杂的 Map-Reduce 模式。
六、总结
QThread
和 QtConcurrent
都是 Qt 提供的强大多线程工具,但使用方式和适用范围不同。
QThread
更加底层、可控,适合复杂线程管理、线程间通信;QtConcurrent
更简洁现代,适合并发计算与短时任务;
场景 | 推荐机制 | 理由 |
---|---|---|
文件/网络下载 | QThread |
需要事件循环、信号槽响应、状态控制 |
图像处理/批量压缩 | QtConcurrent |
任务独立、计算密集、无需交互 |
后台服务守护 | QThread |
线程常驻、需要持续响应 |
数据库查询 | QtConcurrent |
一次性任务,避免阻塞 UI |
UI 按钮触发异步任务 | QtConcurrent |
简洁,方便绑定任务完成回调 |
多任务调度/线程池复用 | QtConcurrent |
自动使用线程池,避免重复造轮子 |