这是我有史以来第一个关于stackoverflow的问题,所以请多多包涵。
我有一个虚拟串行端口USB设备,可以使用QSerialPort(Qt-5.9)与之通信。有很多数据需要发送给它(每40ms大约6 KB),并且还需要读取一些数据。设备每隔几毫秒发送一次数据。
在Mac上,我不能仅转储所有写数据,因为它似乎溢出了Mac的内部写缓冲区。如果发生这种情况,则没有任何指示,并且后续写入将永远失败。一种解决方案是在每次写入后都刷新()。 Windows和Linux可以接受大量写入数据,只要在发送或接收新批次之前有足够的时间将数据输入/输出。换句话说,在Windows和Linux上,不需要阻止通信,但是下面描述的问题仍然存在。
如果我从主线程访问端口,一切都很好。它像魅力一样运转。但是,随着flush()的写入现在被阻止。他们需要时间。我的应用程序需要能够一次与这些设备中的一个或多个通信。在这种情况下,GUI线程受苦。
因此,解决方案是将串行端口访问移至线程。这是乐趣开始的地方。我尝试过:
//////////////////////////////////////////
Device::Device (const QString portName):
QThread (nullptr),
mSerPort (nullptr),
mPortPath (portName)
{
}
//////////////////////////////////////////
bool Device::StartThread (void)
{
// Create and open serial port
mSerPort = new QSerialPort(mPortPath);
mSerPort->moveToThread(this);
mSerPort->open(QIODevice::ReadWrite);
if (mSerPort->isOpen() == false)
{
qDebug() << mSerPort->errorString();
}
// Start thread (execute run())
start();
}
//////////////////////////////////////////
void Device::run (void)
{
while (true)
{
if (mSerPort->isOpen() == true)
{
Read(); // Read data from the device
// Write(); // Write data to the device
}
msleep(THREAD_SLEEP_MS);
}
}
Read()和Write()只是QSerialPort的read()和write()函数的薄包装。没有与其他线程共享数据。
在这种情况下,我在端口open()上收到运行时警告:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QSerialPort(0x109676430), parent's thread is Device(0x10400b0a0), current thread is QThread(0x101503cb0)
这是可以理解的,因为从GUI线程调用了Device :: StartThread()。但是,该应用程序可以运行一会儿,读取(并在未注释Write()的情况下进行写入),但最终会因QSerialPort :: read()内部的访问冲突而崩溃。
因此,我的下一个尝试是将port open()移至run():
//////////////////////////////////////////
void Device::run (void)
{
// Open the serial port
mSerPort->open(QIODevice::ReadWrite);
if (mSerPort->isOpen() == false)
{
qDebug() << mSerPort->errorString();
return;
}
while (true)
{
...
这可确保端口完全在设备线程内部。不幸的是,在这种情况下,Read()永远不会从端口获取任何数据。所有读取返回0字节。没有报告端口错误。
我在做什么错?为什么没有从设备读取数据(我是肯定的,需要读取数据)?
谢谢
答案 0 :(得分:0)
听起来您没有完全将QSerialPort
移到线程中以正确使用它。查阅有关如何从Qt文档中正确创建线程的指南:https://wiki.qt.io/QThreads_general_usage
要了解的重要部分是,许多旧指南告诉您在run
中覆盖QThread
方法-请勿这样做!我无法从您发布的代码中得知您是否继承自QThread
。
如果确实有用于串行通信的单独线程,则还应该能够使用QTimer
来定期发送必须发送到设备的消息,从而进入事件驱动的系统。