当前位置: 首页 > article >正文

Qt 多线程误区:QThread 任务未正确执行及收尾回调未触发的原因分析

问题背景

在开发 Qt 项目时,我们希望并行执行多个任务来提高效率,并在所有任务完成后进行收尾处理。为此,我们使用 QThread 来执行任务,并在 QThread::finished 事件触发时调用收尾函数。

原始代码

void MyClass::runTaskInThread(void (MyClass::*task)(), void (MyClass::*onFinished)())
{
    QThread *thread = new QThread(this);
    connect(thread, &QThread::started, this, task);  // ❌ 错误:task 仍然在主线程执行!
    connect(thread, &QThread::finished, this, [=]() {
        (this->*onFinished)(); // 触发完成回调
        thread->deleteLater(); // 自动清理线程
    });
    thread->start();
}

void MyClass::startTask()
{
    runTaskInThread(&MyClass::task1, &MyClass::task1Finished);
    runTaskInThread(&MyClass::task2, &MyClass::task2Finished);
    runTaskInThread(&MyClass::task3, &MyClass::task3Finished);
}

void MyClass::task1() {}
void MyClass::task2() {}
void MyClass::task3() {}

void MyClass::taskFinishedHandle()
{
    if(task1Finished && task2Finished && task3Finished)
        doSomething();
}

void MyClass::task1Finished() { task1Finished = true; taskFinishedHandle(); }
void MyClass::task2Finished() { task2Finished = true; taskFinishedHandle(); }
void MyClass::task3Finished() { task3Finished = true; taskFinishedHandle(); }

发现的问题

在实际运行中,task1Finishedtask2Finishedtask3Finished从未被执行,导致 taskFinishedHandle 也不会触发。

错误分析:QThread 默认不会运行任务

在 Qt 中,QThread默认不会自动运行任何代码,它只是提供了一个独立的事件循环。

原始代码的关键错误:

connect(thread, &QThread::started, this, task);  // ❌ 错误

这个 connect 绑定的 task 仍然是 this(主线程)的方法,因此 task()仍然在主线程执行,而 QThread 只是空跑了一下,没有执行任何任务。

为什么 QThread 变成了“空壳线程”?

  1. thread->start(); 只是启动了 QThread,进入 事件循环,但 不会自动运行任务
  2. connect(thread, &QThread::started, this, task); 触发 task(),但 this 仍然是 主线程,所以 task()仍然在主线程里执行
  3. task() 运行完后,线程 实际上并没有执行任何代码,于是 QThread 认为工作完成,触发 finished,然后销毁。
总结:你只是新建了一个 QThread ,但任务仍然在主线程执行,导致 QThread 变成了一个“空壳”。

正确的实现方式

方法 1:使用 moveToThread 让任务运行在 QThread

void MyClass::runTaskInThread(void (MyClass::*task)(), void (MyClass::*onFinished)())
{
    QThread *thread = new QThread(this);
    QObject *worker = new QObject();  // 创建一个独立的 worker
    worker->moveToThread(thread); // 让 worker 运行在新线程

    connect(thread, &QThread::started, worker, [=]() {
        (this->*task)();  // 现在 task() 会在新线程中执行
        QMetaObject::invokeMethod(this, onFinished, Qt::QueuedConnection);  // 让 onFinished 在主线程运行
        thread->quit();
    });

    connect(thread, &QThread::finished, thread, &QObject::deleteLater);
    thread->start();
}

方法 2:使用 QtConcurrent::run

#include <QtConcurrent>

void MyClass::runTaskInThread(void (MyClass::*task)(), void (MyClass::*onFinished)())
{
    QtConcurrent::run([=]() {
        (this->*task)();   // 任务会在新线程中执行
        QMetaObject::invokeMethod(this, onFinished, Qt::QueuedConnection);  // 让回调在主线程运行
    });
}

两种方法的区别

方法

适用场景

优势

moveToThread

需要更灵活的线程管理(如持续运行任务)

线程控制权更强,可绑定 QObject 事件循环

QtConcurrent::run

任务是一次性的,无需额外管理线程

代码更简洁,自动管理线程,避免 QThread 内存泄漏

总结

错误原因:

  • 直接 connect(thread, &QThread::started, this, task); 任务仍然在 主线程 运行,导致 QThread没有执行任何代码

正确做法:

  1. moveToThread 确保任务在 QThread 里运行。
  2. 更推荐用 QtConcurrent::run,代码简洁,自动管理线程,不会导致 QThread 泄漏。

如果你的 task1task2task3 仍然卡在主线程,请一定要改用 QtConcurrent::runmoveToThread


http://www.kler.cn/a/560409.html

相关文章:

  • 云计算该如何实现高效数据存储和处理?
  • 【目标检测】目标检测中的数据增强终极指南:从原理到实战,用Python解锁模型性能提升密码(附YOLOv5实战代码)
  • PV和UV的区别
  • UE5从入门到精通之多人游戏编程常用函数
  • Qt中C++与QML交互从原理、方法与实践陷阱深度解析
  • 系统架构分析:软件需求工程部分知识一览概括
  • C++ Primer 额外的string操作
  • LLM高效推理:KV缓存与分页注意力机制深度解析
  • ubuntu windows双系统踩坑
  • 从零掌握动态代理:JDK与CGLib的实现原理与实战应用
  • Redis基操
  • STM32单片机开发(7).离散PID的程序实现
  • Apache Pinpoint工具介绍
  • [实现Rpc] 客户端 | Requestor | RpcCaller的设计实现
  • JVM view(1)
  • rust笔记9-引用与原始指针
  • 浏览器JS打不上断点,一点就跳到其他文件里。浏览器控制台 js打断点,指定的位置打不上断点,一打就跳到其他地方了。
  • 精准识别IP应用场景
  • 【运维】内网服务器借助通过某台可上外网的服务器实现公网访问
  • 玩机日记 12 fnOS使用lucky反代https转发到外网提供服务