Qt线程间数据传递问题:worker的doWork()函数无法启动
问题分析与解决方案
核心问题点
- 线程使用逻辑错误:你的
MyThreadWorker继承了QThread,同时又调用moveToThread将其移到另一个QThread实例中,违背Qt线程设计逻辑——QThread是线程管理者,而非工作对象,正确Worker应继承QObject。 - 线程启动方式错误:主窗口中调用
worker->start(),但worker已被移到子线程,且你重写的start方法并非线程启动的正确路径,应启动thread->start()来触发线程started信号。 - 方法重写冲突:继承
QThread后重写start会覆盖父类的线程启动方法,导致线程无法初始化事件循环,QTimer因此无法工作。
推荐解决方案:Worker继承QObject(官方标准写法)
这种模式逻辑清晰、易于维护,是Qt官方推荐的线程工作方式。
1. 修改Worker头文件
#include <QObject> #include <QTimer> class MyThreadWorker : public QObject { Q_OBJECT public: float x; float y; float z; int correct; explicit MyThreadWorker(QObject *parent = nullptr); private: Adafruit_BNO055 * imu_first; QTimer * timer; signals: void dataReady(float x, float y, float z, int correct); public slots: void doWork(); void startWork(); // 自定义启动槽,避免和QThread的start方法冲突 void stopWork(); // 可选:添加资源清理逻辑 };
2. 修改Worker源文件
#include "MyThreadWorker.h" MyThreadWorker::MyThreadWorker(QObject *parent) : QObject(parent) { } void MyThreadWorker::startWork() { imu_first = new Adafruit_BNO055; imu_first->imu_initialization(); timer = new QTimer(this); // 指定父对象,自动管理内存 connect(timer, &QTimer::timeout, this, &MyThreadWorker::doWork); timer->start(1000); qDebug() << "start work in thread"; } void MyThreadWorker::doWork() { imu_first->sig_bno_handler(); gpioSleep(0, 0, 1000); imu_first->savevector(); x = imu_first->myStruct.x; y = imu_first->myStruct.y; z = imu_first->myStruct.z; correct = imu_first->init_correct; qDebug() << "Dane: " << x; emit dataReady(x, y, z, correct); } void MyThreadWorker::stopWork() { if(timer){ timer->stop(); delete timer; timer = nullptr; } delete imu_first; imu_first = nullptr; }
3. 修改主窗口源文件
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); // 初始化线程与Worker QThread* thread = new QThread(this); MyThreadWorker* worker = new MyThreadWorker(); worker->moveToThread(thread); // 连接信号槽 connect(thread, &QThread::started, worker, &MyThreadWorker::startWork); connect(worker, &MyThreadWorker::dataReady, this, &MainWindow::updateData); // 线程结束时自动清理资源 connect(thread, &QThread::finished, worker, &QObject::deleteLater); connect(thread, &QThread::finished, thread, &QObject::deleteLater); // 启动线程(关键:启动的是thread实例,而非worker) thread->start(); // 滑块范围设置 ui->slider1->setRange(10, 50); ui->slider2->setRange(10, 50); gpioSleep(0, 0, 2000); } void MainWindow::updateData(float x, float y, float z, int correct) { // 主线程中处理数据,可安全操作UI qDebug() << "Received data:" << x << y << z << correct; }
备选方案:修正继承QThread的写法
若坚持使用继承QThread的方式,需重写run方法并启动线程事件循环:
1. 修改Worker头文件
#include <QThread> #include <QTimer> class MyThreadWorker : public QThread { Q_OBJECT public: float x; float y; float z; int correct; MyThreadWorker(); private: Adafruit_BNO055 * imu_first; QTimer * timer; protected: void run() override; // 重写run方法,替代自定义start signals: void dataReady(float x, float y, float z, int correct); private slots: void doWork(); };
2. 修改Worker源文件
#include "MyThreadWorker.h" MyThreadWorker::MyThreadWorker() : QThread() { } void MyThreadWorker::run() { imu_first = new Adafruit_BNO055; imu_first->imu_initialization(); timer = new QTimer(); connect(timer, &QTimer::timeout, this, &MyThreadWorker::doWork); timer->start(1000); qDebug() << "start work in thread"; exec(); // 启动线程事件循环,否则QTimer无法工作 // 清理资源 timer->stop(); delete timer; delete imu_first; } void MyThreadWorker::doWork() { imu_first->sig_bno_handler(); gpioSleep(0, 0, 1000); imu_first->savevector(); x = imu_first->myStruct.x; y = imu_first->myStruct.y; z = imu_first->myStruct.z; correct = imu_first->init_correct; qDebug() << "Dane: " << x; emit dataReady(x, y, z, correct); }
3. 修改主窗口源文件
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); MyThreadWorker* worker = new MyThreadWorker(); connect(worker, &MyThreadWorker::dataReady, this, &MainWindow::updateData); // 线程结束时自动清理 connect(worker, &MyThreadWorker::finished, worker, &QObject::deleteLater); // 启动线程(调用QThread原生start方法,触发run) worker->start(); // 滑块范围设置 ui->slider1->setRange(10, 50); ui->slider2->setRange(10, 50); gpioSleep(0, 0, 2000); } void MainWindow::updateData(float x, float y, float z, int correct) { // 处理数据 qDebug() << "Received data:" << x << y << z << correct; }
关键注意事项
- 所有UI操作必须放在主线程的槽函数中(如
updateData),Qt禁止子线程直接操作UI。 - 跨线程信号槽连接默认使用
Qt::QueuedConnection,可安全传递数据。 - 通过
finished信号连接deleteLater,避免内存泄漏。
内容的提问来源于stack exchange,提问作者hubertkulak
相关产品推荐
相关产品推荐

