【问题标题】:Simultaneous read and write on serial port using 2 threads使用2个线程在串口上同时读写
【发布时间】:2016-11-28 14:31:26
【问题描述】:

在 Qt 中,是否可以从 2 个不同的线程同时读取和写入同一个串口?

【问题讨论】:

  • 不,但无论你认为你需要这样做,这很可能是错误的。当然数据可以来自任何你想要的地方,但是QSerialPort方法的调用必须和port->thread()在同一个线程中进行。
  • @Kuba Ober,我明白了。但这就是问题所在。我正在尝试演示在一个线程上读取串行端口并在 GUI 线程上显示。为了测试这一点,我需要不断地写入串口,同时需要从串口读取相同的数据并显示在 GUI 线程上。我试图在一台机器(我的笔记本电脑)上演示所有这些。所以我想在我的应用程序中有 3 个线程。 1 个主 GUI 线程,1 个读取器线程,1 个写入器线程。
  • 读写器必须是同一个线程。鉴于串行端口的速度相对较慢,与 CPU/内存带宽相比,将作业分成两个线程会使事情变得更糟,而不是更好:拥有多个线程的开销高于保持串行端口填充所需的作业随时提供数据,并处理任何回复。
  • @Kuba Ober,当应用程序在一台机器(我的笔记本电脑)上运行时,我不能让多个应用程序使用串行端口。所以,我想知道是否有多个线程可以随时使用串口。
  • 我看不出这有什么影响。我要说的是您需要从线程访问串行端口。它可以是任何线程:可以是 gui 线程,也可以是专用线程。没关系。但它只能是一个线程:port.thread() 的值。这并不意味着您不能将数据从其他线程传递到该线程,或者提取数据并将其传递到其他地方。这只意味着直接在QSerialPort 上完成的调用必须全部从端口的线程中完成。之后您可以在多个线程中传递这些数据!

标签: multithreading qt


【解决方案1】:

不直接。您在port 上调用的任何方法都必须在port.thread() 内调用。否则是未定义的行为:您可以格式化您的硬盘。

但是您可以通过使用信号槽机制间接地做到这一点。我们不调用端口上的方法,而是有一个接口类作为端口的线程安全接口:

struct PortInterface : QObject {
   Q_SIGNAL void writeData(const QByteArray &);
   Q_SIGNAL void hasReadData(const QByteArray &);
   Q_OBJECT
};

int main(int argc, char ** argv) {
  QApplication app(argc, argv);
  PortInterface interface;
  QSerialPort port;

  QObject::connect(&interface, &PortInterface::writeData, &port, [&](const QByteArray &data){
     qDebug() << "writing in thread" << QThread::currentThread();
     Q_ASSERT(QThread::currentThread() == port.thread());
     port.write(data);
  });
  QObject::connect(&port, &QIODevice::readyRead, [&]{
     qDebug() << "reading in thread" << QThread::currentThread();
     Q_ASSERT(QThread::currentThread() == port.thread());
     emit interface.hasReadData(port.readAll());
  });

您可以在任何线程中调用writeData 方法:Qt 的信号槽机制将包装调用并将其安全地传递到端口的线程。同样,hasReadData 信号可以从任何线程调用。 readAll 调用是从端口自己的线程完成的。处理可用数据的代码应连接到该插槽。

因此,我们可以有一个计时器,在专用线程中计时以将一些数据写入端口,并且我们可以在主线程中拥有一个监听新数据的槽:

 QTimer sourceTimer;
  sourceTimer.start(20);
  QObject::connect(&sourceTimer, &QTimer::timeout, [&]{
     qDebug() << "timer tick in thread" << QThread::currentThread();
     interface.writeData(QByteArray(20, 'd'));
  });
  QObject::connect(&interface, &PortInterface::hasReadData, &app, [&](const QByteArray &data){
     qDebug() << "data read in thread" << QThread::currentThread();
     qDebug() << data.toHex();
  });

  QThread sourceThread, portThread;
  QThread::currentThread()->setObjectName("mainThread");
  sourceThread.setObjectName("sourceThread");
  portThread.setObjectName("portThread");;
  sourceTimer.moveToThread(&sourceThread);
  port.moveToThread(&portThread);
  sourceThread.start();
  portThread.start();
  return app.exec();
}

您可以将任意数量的对象附加到hasReadData 信号。这些对象可以存在于任何线程中。回想一下,信号槽连接属于1:n 类型,其中0&lt;=n

同样,您可以让任意数量的对象调用接口的writeData 方法:只要它们写入的数据是自包含数据包,您就可以保证数据包将在端口上发送为一个单元,不与其他数据包交错。但是,接收方必须能够描述数据包:数据包将需要标头或其他同步方式(例如 HDLC)。

当然你需要先打开端口:)

【讨论】:

  • 你能帮我把这段代码安排到一个Qt项目中吗?至于上面代码的哪一部分去哪里了?
  • @Aham 想象一下散文(描述性文本)不存在。把代码片段粘在一起,你会得到一个可以编译的main.cpp。它不会做太多,因为端口没有打开,但它是完整的。
【解决方案2】:

不是。由于它的实现,不可能从不同的线程进行读/写(与 Qt 的任何 I/O 类相同)。 QSP 使用非阻塞(异步)I/O,允许从一个线程“同时”使用读/写。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-03
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 2023-03-19
    • 2014-07-15
    相关资源
    最近更新 更多