如何实现QThread运行永远{}与QWaitCondition但仍需要捕获另一个插槽,同时 [英] How to implement a QThread that runs forever{} with a QWaitCondition but still needs to catch another Slot while doing that

查看:1368
本文介绍了如何实现QThread运行永远{}与QWaitCondition但仍需要捕获另一个插槽,同时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现了一个类,它可以通过一个插槽从一个串口读取数据到一个串口。我使用QAsyncSerial这反过来使用boost :: asio与回调。
类被移动到一个线程,并且当QThread发出started()时执行它的start()方法

I implemented a class that can write data to a serial port via a QQueue and read from it by a slot. I use QAsyncSerial for this which in turn uses boost::asio with a callback. The class is moved to a thread and its start() method is executed when the QThread emits "started()"

问题是我出队QQueue中的start() - 方法使用forever {}和一个QWaitCondition。虽然这是运行(显然运行永远)连接到dataReceived信号QAsyncSerial的插槽不能被调用,因此我从来没有读过串口的任何东西。

The problem is that I dequeue the QQueue in the start()-method using forever {} and a QWaitCondition. While this is running (which obviously runs forever) the slot connected to the dataReceived signal of QAsyncSerial can not be called, thus I never read anything from the serial port.

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回调:

The changed QAsyncSerial callback used by boost::asio:

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

编辑

我用另一种方法解决了这个问题。我打开QAsyncSerial,而是使用CallbackAsyncSerial,它也直接由QAsyncSerial分发。现在boost :: asio使用的回调是serialSlotReceivedDataslot。这个解决了问题,因为回调在线程boost :: asio中被调用。由于它有自己的线程,所以运行的线程SerialPortHandler被永远循环阻塞是没有关系的。

I solved this problem with another approach. I ditched QAsyncSerial and instead used the CallbackAsyncSerial which is also distributed by QAsyncSerial directly. Now the callback used by boost::asio is the serialSlotReceivedData "slot". This "solves" the problem since the callback is called in the thread boost::asio runs in. Since it has its own thread it doesn't matter that the thread SerialPortHandler runs in is blocked by the forever loop.

新代码:(因为QAsyncSerial就像一个CallbackAsyncSerial的包装器只有一些小事情改变了)

New code: (since QAsyncSerial is something like a wrapper to CallbackAsyncSerial only some trivial things changed)

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(),它将执行这个任务。

1) Create slot in your thread, for example onMessageReady(), which will do the job.

2)创建一个信号表示新消息准备就绪,您创建新消息。

2) Create a signal indicates that new message ready, and emit it each time you creating a new message.

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

3) Connect them using QueuedConnection and call your thread's exec function.

这不会阻塞你的线程,因为WaitforObject会处理所有传入的信号。

This won't block your thread, as WaitforObject does, and you will handle all incoming signals.

像这样:

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。

after all simply call thread's exec() method, you don't need to reimplement run() and to use QWaitCondotion at all.

这篇关于如何实现QThread运行永远{}与QWaitCondition但仍需要捕获另一个插槽,同时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆