问题背景
在开发 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(); }
发现的问题
在实际运行中,task1Finished
、task2Finished
、task3Finished
从未被执行 ,导致 taskFinishedHandle
也不会触发。
错误分析:QThread 默认不会运行任务
在 Qt 中,QThread
默认不会自动运行任何代码,它只是提供了一个独立的事件循环。
原始代码的关键错误:
connect(thread, &QThread::started, this, task); // ❌ 错误
这个 connect
绑定的 task
仍然是 this
(主线程)的方法,因此 task()
仍然在主线程执行 ,而 QThread
只是空跑了一下,没有执行任何任务。
为什么 QThread****变成了"空壳线程"?
thread->start();
只是启动了QThread
,进入 事件循环 ,但 不会自动运行任务。connect(thread, &QThread::started, this, task);
触发task()
,但this
仍然是 主线程 ,所以task()
仍然在主线程里执行。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
没有执行任何代码。
正确做法:
- 用 moveToThread 确保任务在
QThread
里运行。 - 更推荐用 QtConcurrent::run,代码简洁,自动管理线程,不会导致
QThread
泄漏。
如果你的 task1
、task2
、task3
仍然卡在主线程,请一定要改用 QtConcurrent::run
或 moveToThread
!