跳转到内容
新建笔记

Qt Worker Object:队列调用、进度与协作停止

Worker Object 模式把工作代码放在 QObject 中,再将该对象移动到专用 QThread。任务通过队列连接进入工作线程,结果回到主线程接收者;线程对象负责调度,worker 负责业务。

  1. 在主线程创建没有 parent 的 worker,否则不能直接移动到另一个线程。
  2. 调用 moveToThread(),建立任务和结果连接。
  3. 启动线程;默认 QThread::run 会运行事件循环。
  4. 通过队列连接提交任务,不能用 worker->process(...) 代替线程切换。
  5. 任务发出结果或完成信号;结束时停止事件循环,等待线程退出并释放 worker。

QObject 的线程归属决定队列事件由哪个线程处理,普通 C++ 成员调用仍在调用者当前线程执行。工作对象的定时器或异步 socket 也要遵守创建、启动和线程归属要求。Qt 线程与对象

完整例子:有进度、可协作停止的单次任务

跳转到“完整例子:有进度、可协作停止的单次任务”
// main.cpp;Qt6::Core,CMAKE_AUTOMOC=ON
#include <QCoreApplication>
#include <QObject>
#include <QThread>
#include <cassert>
class Worker final : public QObject {
Q_OBJECT
public slots:
void process(int count) {
assert(QThread::currentThread() == thread());
for (int i = 0; i < count; ++i) {
if (QThread::currentThread()->isInterruptionRequested()) break;
QThread::msleep(1); // 用短步骤模拟工作,实际任务需有自己的取消边界
emit progress(i + 1);
}
emit completed();
}
signals:
void progress(int completedCount);
void completed();
};
class Controller final : public QObject {
Q_OBJECT
public:
Controller() {
auto *worker = new Worker;
worker->moveToThread(&workerThread_);
connect(&workerThread_, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::process, Qt::QueuedConnection);
connect(worker, &Worker::progress, this, &Controller::progress, Qt::QueuedConnection);
connect(worker, &Worker::completed, &workerThread_, &QThread::quit);
connect(&workerThread_, &QThread::finished, this, &Controller::finished);
workerThread_.start();
}
~Controller() override {
workerThread_.requestInterruption();
workerThread_.quit();
workerThread_.wait();
}
void start(int count) { emit operate(count); }
void requestStop() { workerThread_.requestInterruption(); }
signals:
void operate(int count);
void progress(int completedCount);
void finished();
private:
QThread workerThread_;
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
Controller controller;
int progress = 0;
QObject::connect(&controller, &Controller::progress, &app, [&](int value) {
assert(QThread::currentThread() == app.thread());
progress = value; // GUI 程序可在这里更新进度控件
});
QObject::connect(&controller, &Controller::finished, &app, &QCoreApplication::quit);
controller.start(5);
const int exitCode = app.exec();
assert(progress == 5);
return exitCode;
}
#include "main.moc"

此例是单次任务控制器:completed 后停止线程,不应对同一实例继续提交新任务。长期任务队列可保留事件循环,并把“某项任务完成”和“整个线程关闭”分开。

finished → deleteLater 是 Qt 提供的 worker 回收方式。析构中的 wait 用于确保线程不会比 Controller 及其资源活得更久;若任务内部长时间不检查中断,关闭仍可能等待,所以不能把无限阻塞的业务代码塞进这个示例。QThread 工作对象示例

为什么停止请求不放进同一个长循环槽

跳转到“为什么停止请求不放进同一个长循环槽”

worker 执行 process() 时占用自己的线程,队列里的另一个 stop() 槽要等当前槽返回才有机会运行。长循环若只能依赖这个 stop 槽改变普通 bool,就可能一直停不下来。

本例使用线程安全的中断请求,在每个小步骤之间检查。需要及时处理多种命令的 worker,应把计算拆成有限批次,在批次之间返回事件循环;quit() 本身不会抢占当前函数。

std::thread 绑定成员函数不是 QObject 迁移

跳转到“std::thread 绑定成员函数不是 QObject 迁移”

std::thread(&SomeClass::task, &object) 或 std::bind 可以在新线程调用成员函数,但不会改变 object 的 Qt 线程归属,也不会自动保护成员、创建 Qt 事件循环或允许操作界面。

原稿 std::thread(... this ...).detach() 还使工作可能在窗口销毁后访问失效的 this。捕获 &yoloWindow 同样要求对象活到线程结束;传入图像路径的值副本只解决路径本身的寿命。立即在 GUI 线程 join() 又会阻塞界面。

Qt GUI 程序可优先使用上面的结果信号模式:后台处理独立数据,主线程按对象寿命接收结果;若必须使用 std::thread,应明确取消、join 和对象寿命协议,不靠 detach 避免管理线程。