繁体   English   中英

如何使用 QWaitCondition 实现一个永远运行的 QThread{},但在执行此操作时仍需要捕获另一个 Slot

[英]How to implement a QThread that runs forever{} with a QWaitCondition but still needs to catch another Slot while doing that

我实现了一个 class,它可以通过 QQueue 将数据写入串行端口并通过插槽从中读取。 我为此使用 QAsyncSerial ,它反过来使用 boost::asio 和回调。 class 被移动到一个线程并在 QThread 发出“started()”时执行它的 start() 方法

问题是我在 start() 方法中使用 forever {} 和 QWaitCondition 使 QQueue 出队。 虽然它正在运行(显然永远运行),但无法调用连接到 QAsyncSerial 的 dataReceived 信号的插槽,因此我从未从串行端口读取任何内容。

解决这个问题的常用方法是什么?

SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QObject(parent), serialPort(serialPort)
{
    m_enqueueMessageMutex = new QMutex();
    m_messageQueue = new QQueue<BaseMessage*>();
    m_waitCondition = new QWaitCondition();
    serial.open(serialPort.deviceName(), 2400);
    connect(&serial, SIGNAL(dataReceived(QByteArray)), this, SLOT(serialSlotReceivedData(QByteArray)));
}

void SerialPortHandler::serialSlotReceivedData(QByteArray line)
{
    qDebug() << QString(line).toAscii();
}

void SerialPortHandler::sendTestPing()
{
    PingMessage *msg = new PingMessage();
    enqueueMessage(msg);
}

void SerialPortHandler::enqueueMessage(BaseMessage *msg)
{
    QMutexLocker locker(m_enqueueMessageMutex);
    m_messageQueue->enqueue(msg);
    m_waitCondition->wakeAll();
}

void SerialPortHandler::start()
{
    if (!serial.isOpen())
        return;

    forever {
        m_enqueueMessageMutex->lock();
        if (m_messageQueue->isEmpty())
            m_waitCondition->wait(m_enqueueMessageMutex);
        BaseMessage *msg = m_messageQueue->dequeue();
        serial.write(msg->encodeForWriting());
        m_enqueueMessageMutex->unlock();
    }
}

boost::asio 使用的更改后的 QAsyncSerial 回调:

void QAsyncSerial::readCallback(const char *data, size_t size)
{
    emit dataReceived(QByteArray::fromRawData(data, (int) size));
}

编辑:

我用另一种方法解决了这个问题。 我放弃了 QAsyncSerial,而是使用了 CallbackAsyncSerial,它也由 QAsyncSerial 直接分发。 现在 boost::asio 使用的回调是 serialSlotReceivedData "slot"。 这“解决”了问题,因为回调在线程 boost::asio 运行中被调用。由于它有自己的线程,因此 SerialPortHandler 运行的线程被永远循环阻塞并不重要。

新代码:(因为 QAsyncSerial 类似于 CallbackAsyncSerial 的包装器,只有一些琐碎的事情发生了变化)

SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QObject(parent), serialPort(serialPort)
{
    m_enqueueMessageMutex = new QMutex();
    m_messageQueue = new QQueue<BaseMessage*>();
    m_waitCondition = new QWaitCondition();
    /* serial is now CallbackAsyncSerial and not QAsyncSerial */
    serial.open(QString(serialPort.deviceName()).toStdString(), 2400);
    serial.setCallback(bind(&SerialPortHandler::serialSlotReceivedData, this, _1, _2));

    m_messageProcessingState = MessageProcessingState::Inactive;
}

void SerialPortHandler::start()
{
    if (!serial.isOpen())
        return;

    forever {
        m_enqueueMessageMutex->lock();

        if (m_messageQueue->isEmpty())
            m_waitCondition->wait(m_enqueueMessageMutex);

        BaseMessage *msg = m_messageQueue->dequeue();
        QByteArray encodedMessage = msg->encodeForWriting();
        serial.write(encodedMessage.constData(), encodedMessage.length());

        m_enqueueMessageMutex->unlock();
    }
}

1) 在您的线程中创建插槽,例如 onMessageReady(),它将完成这项工作。

2)创建信号表示新消息准备就绪,并在每次创建新消息时发出。

3) 使用 QueuedConnection 连接它们并调用线程的 exec function。

这不会像 WaitforObject 那样阻塞您的线程,并且您将处理所有传入的信号。

像这样的东西:

SerialPortHandler: public QThread
{
  Q_OBJECT
...
signals:
    void sNewMessageReady();
slots:
    void onNewMessageReady();
    void serialSlotReceivedData(QByteArray);
};

SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QThread(parent), serialPort(serialPort)
{
    m_enqueueMessageMutex = new QMutex();
    m_messageQueue = new QQueue<BaseMessage*>();
    serial.open(serialPort.deviceName(), 2400);
    connect(&serial, SIGNAL(dataReceived(QByteArray)), this, SLOT(serialSlotReceivedData(QByteArray)));
    connect(this, SIGNAL(sNewMessageReady()), this, SLOT(onNewMessageReady()),Qt::QueuedConnection);
}

void SerialPortHandler::enqueueMessage(BaseMessage *msg)
{
    QMutexLocker locker(m_enqueueMessageMutex);
    m_messageQueue->enqueue(msg);
    emit sNewMessageReady();
}


void SerialPortHandler::onNewMessageReady()
{
    QMutexLocker locker(m_enqueueMessageMutex);
    BaseMessage *msg = m_messageQueue->dequeue();
    serial.write(msg->encodeForWriting());
}

毕竟只是调用线程的 exec() 方法,您根本不需要重新实现 run() 并使用 QWaitCondotion。

这有点像在黑暗中拍摄,因为我对使用 Qt 还很陌生,而且我不知道解决此类问题的“通常”方法,但也许在循环中调用QCoreApplication::processEvents会有所帮助。

除非出于某种原因绝对必要,否则我会摆脱 QWaitCondition。 相反,当有 enqueueMessage() 在将新数据附加到 QQueue 后发出 (Qt) 信号时,并让您的工作线程以通常的 Qt 方式接收该信号(以及它需要接收的任何其他信号)。 然后你的问题就消失了,不需要超时或其他黑客行为。

(可选优化:只有在QQueue为空的情况下,串口才会在添加新数据之前发出信号,并从QQueue中读取主线程对应的slot,直到QQueue为空——这样可以减少需要发送的信号)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM