QT之QThread 与 QtConcurrent

发布于:2025-07-29 ⋅ 阅读:(21) ⋅ 点赞:(0)

在开发中不可避免的会遇到耗时操作、后台计算、异步任务等需求。此时,使用多线程编程便成为解决性能与响应式 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 中使用

 

五、建议

  1. 不要滥用 QThread:不推荐直接在主线程中执行 run();也不建议频繁创建/销毁线程。

  2. 短期任务用 QtConcurrent 更自然:能显著减少代码量,提高可读性。

  3. 任务有 UI 回调?用 QFutureWatcher:避免在 QtConcurrent 的线程中直接访问 UI(线程不安全)。

  4. 需要信号槽?考虑 moveToThread 模式:结合事件循环,线程间通信更强大。

  5. 并行计算/算法任务?可用 QtConcurrent::mapped/filter/reduce:实现更复杂的 Map-Reduce 模式。

六、总结

QThread QtConcurrent 都是 Qt 提供的强大多线程工具,但使用方式和适用范围不同。

  • QThread 更加底层、可控,适合复杂线程管理、线程间通信;

  • QtConcurrent 更简洁现代,适合并发计算与短时任务;

场景 推荐机制 理由
文件/网络下载 QThread 需要事件循环、信号槽响应、状态控制
图像处理/批量压缩 QtConcurrent 任务独立、计算密集、无需交互
后台服务守护 QThread 线程常驻、需要持续响应
数据库查询 QtConcurrent 一次性任务,避免阻塞 UI
UI 按钮触发异步任务 QtConcurrent 简洁,方便绑定任务完成回调
多任务调度/线程池复用 QtConcurrent 自动使用线程池,避免重复造轮子

 


网站公告

今日签到

点亮在社区的每一天
去签到