You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QThread中调用QSerialPort::waitForReadyRead()无法读取数据求助

问题原因及解决方案

这个问题我之前踩过坑,核心原因是Qt里QObject的线程亲和性在作祟!

为什么读不到数据?

QSerialPort是QObject的子类,所有QObject对象都有一个“线程亲和性”——它只能在创建它的线程中安全地调用成员函数。你在MainWindow(主线程)中创建了serial对象,并且把它的指针传给了子线程WorkerThread,然后在子线程里调用thread_serial->waitForReadyRead(),这完全违反了Qt的线程模型:

  • QSerialPort的I/O操作依赖于它所在线程的事件循环,子线程无法触发主线程中串口对象的事件处理逻辑;
  • 跨线程直接调用QObject的非线程安全成员函数,行为是未定义的,大概率会出现各种异常(比如读不到数据、程序崩溃)。

另外你代码里还有个小细节:串口名写的是"COM 4"(带空格),Windows系统下正确的串口名应该是"COM4",这个也可能导致串口连接的隐性问题。

解决方案

这里提供两种靠谱的解决思路,根据你的需求选择:

方案1:把串口的创建、打开都移到子线程中

让串口对象的线程亲和性直接属于子线程,这样在子线程里调用阻塞读取函数就完全合法了:

修改WorkerThread类:

class WorkerThread : public QThread {
    Q_OBJECT
public:
    void run() override {
        // 在子线程中创建串口对象,不要设置parent
        QSerialPort *thread_serial = new QSerialPort();
        thread_serial->setPortName("COM4");
        thread_serial->setBaudRate(QSerialPort::Baud115200);
        thread_serial->setDataBits(QSerialPort::Data8);
        thread_serial->setParity(QSerialPort::NoParity);
        thread_serial->setStopBits(QSerialPort::OneStop);
        thread_serial->setFlowControl(QSerialPort::NoFlowControl);
        
        if (thread_serial->open(QIODevice::ReadWrite)) {
            qDebug()<<"Serial port opened in worker thread";
            // 现在可以安全调用阻塞读取函数
            int ret = thread_serial->waitForReadyRead(60000);
            if (ret) {
                qDebug()<<"Data received";
                QByteArray recvData = thread_serial->readAll();
                qDebug()<<recvData;
                emit progressChanged();
            }
            thread_serial->close();
        } else {
            qDebug()<<"Failed to open serial port in thread";
        }
        delete thread_serial;
    }
signals:
    void progressChanged();
};

然后修改MainWindow的openSerialPort函数,不用在主线程创建串口了:

void MainWindow::openSerialPort( )
{
    qDebug()<<"Starting worker thread";
    WorkerThread *workerThread = new WorkerThread();
    connect(workerThread, SIGNAL(progressChanged()), this, SLOT(onProgressChanged()));
    // 线程结束后自动清理
    connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
    workerThread->start();
}

方案2:用信号槽异步传递数据(更符合Qt的事件驱动模型)

保持串口在主线程中,通过readyRead信号触发数据读取,再把数据传递给子线程处理,避免阻塞操作:

修改WorkerThread类,添加处理数据的槽函数:

class WorkerThread : public QThread {
    Q_OBJECT
public slots:
    // 子线程中处理数据的槽函数
    void processSerialData(const QByteArray &data) {
        qDebug()<<"Data received in thread:"<<data;
        emit progressChanged();
    }
protected:
    void run() override {
        // 启动子线程的事件循环,才能接收槽函数调用
        exec();
    }
signals:
    void progressChanged();
};

修改MainWindow的openSerialPort函数,连接串口的readyRead信号:

void MainWindow::openSerialPort( )
{
    serial = new QSerialPort(this);
    serial->setPortName("COM4");
    serial->setBaudRate(QSerialPort::Baud115200);
    serial->setDataBits(QSerialPort::Data8);
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setFlowControl(QSerialPort::NoFlowControl);
    
    if (serial->open(QIODevice::ReadWrite)) {
        qDebug()<<"Connected";
        WorkerThread *workerThread = new WorkerThread();
        // 串口收到数据时,读取并传给子线程处理
        connect(serial, &QSerialPort::readyRead, this, [this, workerThread](){
            QByteArray data = serial->readAll();
            // 用invokeMethod跨线程调用子线程的槽函数
            QMetaObject::invokeMethod(workerThread, "processSerialData", 
                                      Qt::QueuedConnection, 
                                      Q_ARG(QByteArray, data));
        });
        connect(workerThread, SIGNAL(progressChanged()), this, SLOT(onProgressChanged()));
        connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
        workerThread->start();
    }
}

关键注意事项

  • 永远不要跨线程直接调用QObject的非线程安全成员函数,QSerialPort的所有I/O相关函数都不是线程安全的;
  • 如果一定要把已创建的QObject移到其他线程,必须确保:
    1. 该对象没有父对象;
    2. 在移动前没有调用过任何可能触发事件循环的函数;
    3. 使用moveToThread()方法完成线程迁移。

内容的提问来源于stack exchange,提问作者Sreejith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:58:12