我实现了一个类,它可以通过QQueue将数据写入串口并通过插槽读取。我使用QAsyncSerial来反过来使用boost :: asio和回调。 该类被移动到一个线程,当QThread发出“started()”时执行它的start()方法
问题是我使用forever {}和QWaitCondition在start()方法中将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();
}
}
答案 0 :(得分:3)
1)在你的线程中创建插槽,例如onMessageReady(),它将完成这项工作。
2)创建一个信号表示新消息准备就绪,并在每次创建新消息时发出。
3)使用QueuedConnection连接它们并调用线程的exec函数。
这不会像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。
答案 1 :(得分:2)
这是一个黑暗中的镜头,因为我很擅长使用Qt而我不知道像这样的问题的“通常”方法,但也许是对QCoreApplication::processEvents
内的问题的调用。循环会有所帮助。
答案 2 :(得分:2)
除非出于某种原因严格必要,否则我将摆脱QWaitCondition。相反,当enqueueMessage()在将新数据附加到QQueue之后发出(Qt)信号,并让您的工作线程以通常的Qt方式接收该信号(以及它需要接收的任何其他信号)。然后你的问题消失了,不需要超时或其他hackery。
(可选优化:如果QQueue在添加新数据之前是空的,串口只发出信号,并且从QQueue读取主线程的相应插槽,直到QQueue为空 - 这可以减少需要发送的信号数量)