QSerialPort::readyRead vs QSerialPort::waitForReadyRead
-
I need to get data that is sent continually by a USB device.
The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
However, I've read some topics (for example: https://stackoverflow.com/a/30492309) that say this is not the "good" solution. Also, this strategy is making my CPU usage jump from zero to 30% without any other processes running in the program.I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.
My questions are:
Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?Here's my code:
MainClass::MainClass() { deviceThread *d = new deviceThread; d->start(); //... other objects and connections }
#include <QSerialPort> class deviceThread : public QThread { Q_OBJECT public: deviceThread(); void run(); private: QSerialPort *serial; void connectionMethod(); void readData(); bool connectionStatus = false; //... other declarations }
deviceThread::deviceThread() { serial = new QSerialPort(this); // followed by serial port configuration } void deviceThread::run() { while(true) if(!connectionStatus) connectionMethod(); //this method works properly else readData(); } void deviceThread::getNewData() { QByteArray newData; if(serial->waitForReadyRead()) newData = serial->readAll(); //etc... }
@Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:
My questions are:
Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?Have a look at this example and see the description. There are two ways of dealing with the data: Asynchronous and synchronous.
ThewaitForReadReady
call blocks, and waits until the data comes in before you can continue,
The async-way uses signals to notify every time data is ready without blocking anything else. So you can collect your bytes and do your further processing.
I would recommend to use the async-approach but the blocking-thread-approach has also its use cases. Depends on your app and the rest of your code.
For example, if you add your SP-connection to a GUI class, it would cause the GUI to freeze, if you usewaitForReadReady()
.Edit:
I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.
Any error message in
errorString()
? -
I need to get data that is sent continually by a USB device.
The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
However, I've read some topics (for example: https://stackoverflow.com/a/30492309) that say this is not the "good" solution. Also, this strategy is making my CPU usage jump from zero to 30% without any other processes running in the program.I've tried to switch to the strategy of connecting the readyRead() signal to my main function, but in this case, for an unknown reason, the program would read the data from the device just 5 times and then would stop reading.
My questions are:
Is it ok to use QSerialPort::waitForReadyRead() at all? If so, why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?Here's my code:
MainClass::MainClass() { deviceThread *d = new deviceThread; d->start(); //... other objects and connections }
#include <QSerialPort> class deviceThread : public QThread { Q_OBJECT public: deviceThread(); void run(); private: QSerialPort *serial; void connectionMethod(); void readData(); bool connectionStatus = false; //... other declarations }
deviceThread::deviceThread() { serial = new QSerialPort(this); // followed by serial port configuration } void deviceThread::run() { while(true) if(!connectionStatus) connectionMethod(); //this method works properly else readData(); } void deviceThread::getNewData() { QByteArray newData; if(serial->waitForReadyRead()) newData = serial->readAll(); //etc... }
@Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:
The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
This means you started with asynchronous, then turned it into synchronous, and then put that into a thread so it would run asynchronous :)
Everything as @Pl45m4 says.
waitForReadyRead()
Just seems a waste for whatever your use case. -
@Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:
The solution I'm using now uses QSerialPort::waitForReadyRead() in a while(true) loop inside a QThread.
This means you started with asynchronous, then turned it into synchronous, and then put that into a thread so it would run asynchronous :)
Everything as @Pl45m4 says.
waitForReadyRead()
Just seems a waste for whatever your use case.Running
waitForReadReady
in a separate thread is correct. Even if you block, you want to block its own thread not any other thread. But the the thread where you use your blocking SP-connection cant be the GUI thread, of course :)The question is, which way fits more in @Linhares use case, since both ways got tested
-
Running
waitForReadReady
in a separate thread is correct. Even if you block, you want to block its own thread not any other thread. But the the thread where you use your blocking SP-connection cant be the GUI thread, of course :)The question is, which way fits more in @Linhares use case, since both ways got tested
@Pl45m4 said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:
Running waitForReadReady in a separate thread is correct.
I think you misunderstand. I did not say it is not "correct". I said it is a "shame" to take an initially asynchronous call, make it synchronous and put it in a thread, when you could work with the initial asynchronous call in the first place.
-
For what it's worth, an idle loop to waitforReadyRead will of course work, but it breaks the framework paradigm, and therefore is discouraged.
-
@Linhares said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:
why is the CPU usage so high? Is there a more efficient solution for reading data from the serial port?
Sometimes it is better to use a timer, and to poll the serial port, because the device driver is able to buffer incoming data much more efficiently than a user mode process. Something like waitForReadReady() will return as soon as there is any data available. It may be only one byte. If you have a real-time requirement to respond to each byte within milliseconds of it arriving, this would be the way to do it, but you may have the overhead of a kernel and device driver switch thousands of times a second. If you are, say, updating a user interface to show the incoming data, then 1/10 second (or even 1 sec) latency may be acceptable. If you set a timer to trigger every 100ms and read all the available data each time (maybe a kbyte at a time) then the operating system overheads are much lower.
-
@bee65
I get what you are saying but it's still a naive approach. Never use timing as an indicator of expected data on a port (at user level). Always consider serial data to be a "bursty" stream of characters, and build your string accordingly, letting the OS tell you what is available and when. This is where a solid serial protocol and skill in stream parsing becomes necessary.Don't misconstrue the above to mean that you never use timers, but for normal async serial data, shouldn't be concerned about timing of character receipt.
and for what it's worth, when a programmer gives me a serial protocol that mentions "timing between messages" I tend to hit them with a rolled up newspaper and say "bad engineer, no cookie!"
-
OK, let me try again to answer the question
why is the CPU usage so high?
The code given may cause kernel context switching for every byte of data received.
Is there a more efficient solution for reading data from the serial port?
Use your knowledge of the latency requirements of the application to make less frequent system calls. Note that this has nothing at all to do with the timing of data arrival. For example, if you are reading serial data from GPS, you may decide you want to draw a blue dot on the screen in less than a second. If you are reading the same data for control of an autonomous vehicle, the requirement would be to process it much more quickly.
I've not use the QSerialPort blocking API myself, but I suggest something like this:
QByteArray deviceThread::newData; void deviceThread::getNewData() { if (serial->waitForReadyRead()) { QThread::wait(100); newData = serial->readAll(); //etc... } }
The call to QThread::wait allows some time for some incoming data to be buffered in the device driver, so that the next call to readAll() can return more data.
Also, you don't want to allocate and free the newData for every read. Move it to a deviceThread class member or elsewhere out of the loop.
The reason I would be leery of using the blocking API in a thread is it is much more complex to close it down correctly. Is it OK to call serial->close() from another thread? Does this cause waitForReadyRead() to unblock and return? I don't know...
-
@bee65 ,
no, your suggestion has not a sense in relation to the
QSP
.- The
QSP::readAll()
does not call the system'sread
API, it just reads the data from the internalQSP
's buffer. - The system's
read
does called automatically when theRX
event occurred and then stores the data to the internalQSP
's buffer.
The code given may cause kernel context switching for every byte of data received.
No, it is a usual way to do reading a data. An
one byte
or nonone byte
completelly depends on the device driver. If a driver is ugly implemented, then it is not a problem ofQSP
.For example, how will you read a data from a socket? In the same place, byte-by-byte can also come. So what? ))
For
QSP
, you can reduce the context switch by getting theQSP::handle()
and setting the read timeout using the systemAPI
(there you can configure this depending on theOS
in different ways). But this approach will be close to a blocking, because the system'sread
call then will wait for a some time (or some bytes).Other ways (even a polling, I mean about using the
QTimer
and to call theQSP::readAll()
by a timeout) will not work withQSP
, in that case you need to implementYourBestQSP
.UPD: From the other side e.g. for your custom QSP implementation , the QThread::wait(100500) can cause the driver's FIFO overflow and then you will lose a data. ))
- The
-
@bee65 ,
no, your suggestion has not a sense in relation to the
QSP
.- The
QSP::readAll()
does not call the system'sread
API, it just reads the data from the internalQSP
's buffer. - The system's
read
does called automatically when theRX
event occurred and then stores the data to the internalQSP
's buffer.
The code given may cause kernel context switching for every byte of data received.
No, it is a usual way to do reading a data. An
one byte
or nonone byte
completelly depends on the device driver. If a driver is ugly implemented, then it is not a problem ofQSP
.For example, how will you read a data from a socket? In the same place, byte-by-byte can also come. So what? ))
For
QSP
, you can reduce the context switch by getting theQSP::handle()
and setting the read timeout using the systemAPI
(there you can configure this depending on theOS
in different ways). But this approach will be close to a blocking, because the system'sread
call then will wait for a some time (or some bytes).Other ways (even a polling, I mean about using the
QTimer
and to call theQSP::readAll()
by a timeout) will not work withQSP
, in that case you need to implementYourBestQSP
.UPD: From the other side e.g. for your custom QSP implementation , the QThread::wait(100500) can cause the driver's FIFO overflow and then you will lose a data. ))
@kuzulis
Thank you for this clarification. I am not a hardware person, but followed this thread for my own interest.The system's read does called automatically when the RX event occurred and then stores the data to the internal QSP's buffer.
This is what I wondered about. Qt's
readyRead()
signal is being emitted without the user program issuing any kind ofread...()
statement. I am guessing that is tied to the underlying provider's notification that new data has arrived? - The
-
@JonB said in QSerialPort::readyRead vs QSerialPort::waitForReadyRead:
I am guessing that is tied to the underlying provider's notification that new data has arrived?
The QSP implemented in the following way:
- QSP::open() does the following:
- opens the device by a name.
- gets the device descriptior.
- subscribes on the
Rx
andTx
system events on a given device descriptor.
- When the
Rx
event occurred, then:
- calls the system non-blocking read call
- copies the read data to the internal QSP buffer.
- right now the QSP::bytesAvailable() makes sense (it resurns a number of bytes from the internal QSP buffer).
- emits the readyRead signal
- The user's QSP::read() (or readAll()) reads a data only from the internal QSP buffer.
So, the QSP automatically collects all received data after it's opening until the user will call the QSP::read() or until the RAM reached (if the QSP's read buffer size is not set). ))
And yes, the QSP::readyRead() will be emitted each time when the new portion of a data arrives. And the user may call only the QSP::bytesAvailable() to see, how many bytes are ready to read by QSP::read(). Or the user may not call QSP::read() or something else, if it not want, the recceived data will not lose.
-
I'm surprised this works at all.
The QSerialPort instance is created in the deviceThread constructor and therefor it lives in the main GUI thread.
Run method may or may not actually override, who knows, as the compiler check is circumvented with the missing
override
identifier.In case it is the actual overridden run method, you now call connect and read/write on the QSerialPort from a different thread which usually results in
QObject::startTimer: timers cannot be started from another thread
warnings and not running the timerswhich would explain your high cpu uptime, as waitForReadyRead now returns immediately and you end up with essentially a
while(true){}
situation consuming the whole core