Worker Object 模式把工作代码放在 QObject 中,再将该对象移动到专用 QThread。任务通过队列连接进入工作线程,结果回到主线程接收者;线程对象负责调度,worker 负责业务。
调用流程与所有权
跳转到“调用流程与所有权”- 在主线程创建没有 parent 的 worker,否则不能直接移动到另一个线程。
- 调用
moveToThread(),建立任务和结果连接。 - 启动线程;默认 QThread::run 会运行事件循环。
- 通过队列连接提交任务,不能用
worker->process(...)代替线程切换。 - 任务发出结果或完成信号;结束时停止事件循环,等待线程退出并释放 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_OBJECTpublic 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_OBJECTpublic: 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 避免管理线程。