2

In Qt, is it possible to simultaneously read from and write to the same serial port from 2 different threads ?

Aham
  • 49
  • 1
  • 8
  • No, but whatever makes you think you need to do that is most likely in error. Of course the data can come from wherever you want, but the invocations of `QSerialPort` methods must all be made from the same thread as `port->thread()`. – Kuba hasn't forgotten Monica Nov 28 '16 at 18:56
  • @Kuba Ober, I get you. But Here's the problem. I am trying to demonstrate serial port read on one thread and display on GUI thread. For testing this, I need to continuously write to the serial port and at the same time the same data needs to be read from serial port and displayed on the GUI thread. And I'm trying to demonstrate all this on a single machine (my laptop). So I thought of having 3 threads in my application. 1 main GUI thread, 1 reader thread, 1 writer thread. – Aham Nov 28 '16 at 19:34
  • The reader and writer must be the same thread. Given the relatively slow speeds of serial ports, compared to the CPU/memory bandwidth, splitting the job into two threads would make things perform worse, not better: the overhead of having multiple threads is higher than the job needed to keep the serial port stuffed with data at all times, and of processing any replies. – Kuba hasn't forgotten Monica Nov 28 '16 at 19:39
  • @Kuba Ober, I cannot have more than one application use the serial port at any time, when the applications are running on a single machine (my laptop). So, I am trying to know if more than one thread can use the serial port at any time. – Aham Nov 28 '16 at 19:39
  • I don't see how that figures in anything. All I'm saying is that you need to access the serial port from a thread. It can be any thread: it can be the gui thread, or a dedicated thread. It doesn't matter. But it can only be one thread: the value of `port.thread()`. That doesn't mean that you can't pass data to that thread from some other thread, or extract data and pass it elsewhere. That only means that calls done directly on the `QSerialPort` must all be done from the port's thread. You can pass that data around many threads afterwards! – Kuba hasn't forgotten Monica Nov 28 '16 at 19:41
  • @Kuba Ober, OK. What do you suggest I do to simulate a scenario where there is continuous data read from serial port and displayed on the GUI? How should I be continuously writing to the serial port, so it can be continuously read and displayed in parallel (at the same time). Working with a single machine is the constraint. – Aham Nov 28 '16 at 19:52
  • I don't see how the number of machines figures in this. Forget about that aspect. All you need is signal-slot connections. It's trivial stuff. Instead of talking about it, try it out. Prototype some code! I don't understand what you mean by simulating: do you have some actual serial port hardware? If not, then you can use a `QIODevice`-based [pipe](http://stackoverflow.com/q/32317081/1329652). There's also a [device simulator example](http://stackoverflow.com/a/32595398/1329652) that uses said pipe, and it supports multithreading you seek. – Kuba hasn't forgotten Monica Nov 28 '16 at 21:46
  • Your question at this point is **very** deficient and unclear. Every back-and-forth we have indicates the poor quality of the question. Please rework it to make it fully understandable of what exactly your requirements are, **why** they are that way - what's the end application, etc. – Kuba hasn't forgotten Monica Nov 28 '16 at 21:48
  • @KubaOber, Thanks. I'm going through the solutions you suggested. I'll start another thread with more details into my requirements as you suggested. – Aham Nov 29 '16 at 07:49

2 Answers2

2

Not directly. Any methods you call on port must be called within port.thread(). Doing otherwise is undefined behavior: you may format your hard drive.

But you can do it indirectly by using the signal-slot mechanism. Instead of calling the methods on the port, let's have an interface class that acts as a thread-safe interface to the port:

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());
  });

You can call the writeData method in any thread: Qt's signal-slot mechanism will wrap the call and safely deliver it to the port's thread. Similarly, the hasReadData signal can be called from any thread. The readAll call is done from port's own thread. The code that processes available data should connect to that slot.

Thus, we can have a timer that ticks in a dedicated thread to write some data to the port, and we can have a slot that listens to new data in the main thread:

 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();
}

You can have any number of objects attached to the hasReadData signal. These objects can live in any thread. Recall that signal-slot connections are of 1:n kind, where 0<=n.

Similarly, you could have any number of objects call the writeData method of the interface: as long as the data they write is a self-contained packet, you're guaranteed that the packet will be sent on the port as a unit, without interleaving with other packets. The receiver must be able to delineate the packets, though: the packets will need a header or other means of synchronization (e.g. HDLC).

Of course you need to open the port first :)

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • can you help me arrange this code into a Qt project? As to which part of the code above goes where? – Aham Nov 29 '16 at 11:31
  • @Aham Imagine that the prose (the descriptive text) wasn't there. Stick the code fragments together and you'll get a `main.cpp` that can be compiled. It won't do much because the port isn't open, but it's complete otherwise. – Kuba hasn't forgotten Monica Nov 29 '16 at 13:55
1

Not. It is not possible to do read/write from different threads due to its implementation (same as for any of I/O classes of Qt). QSP uses the non-blocking (asynchronous) I/O that allows to use read/write from one thread "simultaneous".

kuzulis
  • 11
  • 1