Qt5 多线程:信号仅以一种方式工作

Posted

技术标签:

【中文标题】Qt5 多线程:信号仅以一种方式工作【英文标题】:Qt5 multi-threading: signals work in one-way only 【发布时间】:2017-08-07 18:47:28 【问题描述】:

这里:Qt multi-thread with GUI 我已经学习了如何创建后台工作程序(Engine 类)。

在那个类中,我有一个QSerialPort 对象,在主线程中运行(请参阅How to setup QSerialPort on a separate thread?)。

我正在使用信号/槽机制来发送/接收数据。但它只能以一种方式工作。 Engine 对象(“工作线程”)发出的信号由QSerialPort(“主线程”)接收。反之亦然:QSerialPort 发出的任何信号都不会被Engine 接收。

engine.h

#ifndef ENGINE_H
#define ENGINE_H

#include <QObject>
#include <QTimer>

#include "myserial.h"

class Engine : public QObject

    Q_OBJECT

public:
    explicit Engine(QObject *parent = 0);

private:
    QTimer m_timer;
    MySerial m_serial;

signals:
    void serialSendMessage(QByteArray data);

private slots:
    void lineReceived(QByteArray line);
    void foo();

public slots:
    void run();
    void open(QString port, quint32 baudrate);
    void close();

;

#endif // ENGINE_H

engine.c

#include "engine.h"
#include <QDebug>

Engine::Engine(QObject *parent) : QObject(parent)

    connect(&m_timer, &QTimer::timeout, this, &Engine::foo);
    m_timer.setInterval(200);


// THIS IS NEVER EXECUTED!
void Engine::lineReceived(QByteArray line)

    qDebug() << line;


// THIS IS RECEIVED BY QSERIALPORT
void Engine::foo()

    emit serialSendMessage("Hello World!");


void Engine::run()

    // THIS DOESN'T WORK!
    connect(&m_serial, &MySerial::lineReceived, this, &Engine::lineReceived);

    // THIS WORK!
    connect(this, &Engine::serialSendMessage, &m_serial, &MySerial::sendMessage);


void Engine::open(QString port, quint32 baudrate)

    m_serial.open(port, baudrate);

    QTimer::singleShot(0, &m_timer, static_cast<void (QTimer::*)(void)>(&QTimer::start));


void Engine::close()

    m_serial.close();

MySerial.h

#ifndef MYSERIAL_H
#define MYSERIAL_H

#include <QObject>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>

class MySerial : public QSerialPort 
    Q_OBJECT

public:
    explicit MySerial(QObject *parent = 0);
    bool open(QString port, quint32 baudrate);
    using QSerialPort::open;
    QByteArray sendMessage(QByteArray data, bool nmea);

signals:
    void lineReceived(QByteArray line);

private slots:
    void onReadyRead();
;

#endif // MYSERIAL_H

MySerial.c

#include "myserial.h"
#include <QDebug>

MySerial::MySerial(QObject *parent) : QSerialPort(parent) 


bool MySerial::open(QString port, quint32 baudrate)

    disconnect(this, 0, 0, 0);
    connect(this, &FemtoSerial::readyRead, this, &MySerial::onReadyRead);

    setPortName(port);
    if (!open(QIODevice::ReadWrite)) return false;
    setDataBits(QSerialPort::Data8);
    setParity(QSerialPort::NoParity);
    setStopBits(QSerialPort::OneStop);
    setBaudRate(baudrate);
    setFlowControl(QSerialPort::NoFlowControl);
    return true;


void MySerial::onReadyRead() 
    static QList<QByteArray> lines;
    static QByteArray buffer;

    buffer += readAll();
    int index = buffer.indexOf("\r");
    while (index != -1) 
        lines.append(buffer.left(index + 1));
        buffer = buffer.mid(index + 1);
        index = buffer.indexOf("\r");
    

    // THIS SIGNAL IS EMITTED!
    while (!lines.isEmpty()) emit lineReceived(lines.takeFirst());


QByteArray MySerial::sendMessage(QByteArray data) 
    write(data);
    return data;

编辑

尝试添加QEventLoop

void Engine::run()

    QEventLoop loop;
    connect(&m_serial, &MySerial::lineReceived, this, &Engine::lineReceived);
    connect(this, &Engine::serialSendMessage, &m_serial, &MySerial::sendMessage);    
    loop.exec();

行为相同:发送数据,但从不执行接收槽。

【问题讨论】:

void Engine::run() 连接几个信号并立即结束其线程。正如我们在上一个问题中告诉您的那样,它应该具有事件循环。 抱歉,我没有完全理解您的 cmets。我正在重新阅读它们和相关链接。 我放弃了,我不明白我在哪里以及为什么需要QEventLoop。我将整个Engine 移动到另一个线程中,我没有将QThread 子类化(我试图捕捉finished 信号并且它不会触发)。此外,发射的信号起作用。无论如何,在run() 函数中添加QEventLoop 不会改变行为。你介意澄清一下发生了什么吗? 当 void Engine::run() 完成其工作时,Engine 线程完成。调用它的基类。 QThread::run() 在退出之前进行事件循环。 @Mark,尽量不要使用额外的线程。使用所有可用的信号和插槽,在条件成立之前永远不要阻塞线程。这会让事情变得更清楚。 【参考方案1】:
bool MySerial::open(QString port, quint32 baudrate)

    //<s>disconnect(this, 0, 0, 0);</s> // <--- strikeout
    connect(this, &FemtoSerial::readyRead, this, &MySerial::onReadyRead);
    // ...

来自文档:

断开连接到对象信号的所有东西

断开连接(myObject, 0, 0, 0);

这意味着来自myObject 的信号,而不是相反。 该行阻止Engine 中的槽被执行,因为它刚刚与源信号断开连接。

Engine 可能会在端口关闭时断开连接,以避免下次打开时出现多个连接。

【讨论】:

以上是关于Qt5 多线程:信号仅以一种方式工作的主要内容,如果未能解决你的问题,请参考以下文章

Mudblazor DatePicker 绑定仅以一种方式工作

C++学习笔记:高级编程:模板,预处理器,信号处理,多线程,Web编程

C# TCP 聊天服务器:连接仅以一种方式工作

Qt多线程:QtConcurrent + QFuture + QFutureWatcher

多线程同步精要

多线程编程