簡體   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