Solved Timing and data issues with SIGNAL/SLOT and QThread
-
This is undoubtedly a simple oversight on my part, but I'm really scratching my head on this one. I have a class which writes data to registers of some serial devices (herein referred to as classRegister). This class is run on a separate thread, which is managed by a dedicated thread handling class (herein referred to as threadHandler). My main class (herein referred to as classMain) establishes a SIGNAL/SLOT connect(), which will pass the required data from the main thread to the classRegister thread.
My current code (trimmed for the purposes of keeping it readable/simple) is as follows:
classMain.h:
class classMain : public QObject { Q_OBJECT public: explicit classMain(QObject *parent = nullptr); Q_PROPERTY(QVariantList array READ array NOTIFY arrayChanged) Q_PROPERTY(unsigned char fig READ fig NOTIFY figChanged) public: QVariantList array(){return this->m_array;} unsigned char fig(){return this->m_fig;} signals: void sendData(QVariantList arrayData, unsigned char figData); public: QVariantList m_array; unsigned char m_fig; };
classRegister.h:
class classRegister : public QObject { Q_OBJECT public: explicit classRegister(QObject *parent = nullptr); int writeData(QVariantList arrayReceived, unsigned char figReceived); };
threadHandler.h:
class threadHandler : public QObject { Q_OBJECT public: explicit threadHandler(QObject *parent = nullptr); public slots: void return(QVariantList arrayReceived, unsigned char figReceived); };
classMain.cpp:
QThread* registerThread = new QThread(this); threadHandler* handler = new threadHandler(); handler->moveToThread(registerThread); connect(this, SIGNAL(sendData(QVariantList, unsigned char)), handler, SLOT(return(QVariantList, unsigned char))); registerThread.start(); //Later on, once the required data has been captured qDebug() << "classMain"; emit sendData(m_array, m_fig);
threadHandler.cpp:
threadHandler::threadHandler(QObject *parent) : QObject(parent) { } void threadHandler::return(QVariantList arrayReceived, unsigned char figReturned) { qDebug() << "threadHandler"; classRegister classRegisterObject; classRegisterObject.writeData(arrayReceived, figReturned); }
I have two main issues at the moment.
Issue 1: As demonstrated in the above pseudo code, each time the classMain function runs, it emits the sendData signal. I.e. there should be a 1:1 relationship between execution of the classMain function and execution of the threadHandler return() SLOT. I've added the basic qDebug()'s in the example code to determine whether this is in fact the case. The qDebug() output when the application executes looks something like this:
classMain threadHandler classMain classMain classMain classMain classMain threadHandler classMain classMain classMain classMain classMain classMain threadHandler
In other words, the emit SLOT isn't called nearly as frequently as it should be.
Issue 2: the QVariantList created in classMain is NOT the same QVariantList received by the threadHandler return() SLOT (and by extension the classRegister writeData() function). I have verified this by putting a qDebug() << array; immediately before calling emit, and a subsequent qDebug() << arrayReceived; in both the threadHandler return() and classRegister writeData() functions. This issue appears to be related to issue 1, as whatever QVariantList data is qDebugged on the threadHandler/classRegister side is completely out of sync with the actual values generated in classMain. E.g. if m_array = [("5, 5", 5")], the threadHandler/classRegister qDebug() might be [("0, 0, 0")], which may have been an accurate value, but from several iterations ago.
The really confusing part? The unsigned char fig appears to be working fine? It has the exact same header setup, the exact same initialisation, etc., and the value of m_fig in classMain and figReceived appears to be accurate. Having said that, the fig value is largely static, so I suspect it is suffering the same issues as the QVariantList, but it's not as noticeable as the value rarely changes.
Is this the best way to provide the data to the classRegister function, whilst making use of the separate thread? If I pass the data directly from classMain to classRegister (i.e. classRegisterObject.writeData(m_array, m_fig)), writing to the device register is perfect, but the GUI is extremely slow (main thread). Instead of passing the data through the SIGNAL/SLOT mechanism itself, would I be better off simply calling array() and fig() from classRegister, and have it return the m_array and m_fig data itself? I've tried this approach as well, and had the same result. I.e. the returned m_array data was not the same as the m_array data being qDebugged in real time in classMain.
Any guidance here would be greatly appreciated!
-
So I've done a bit more digging, and it looks like the classRegister code takes around 100ms to execute. The emit signal in classMain is being emitted at 60Hz, so there's obviously an issue there (60Hz vs. 10Hz). The classRegister function is only writing 84 bytes of data, so I'd be very surprised if I can't push at least 60Hz out of that function.
-
This all appears to be working now. I've optimised my code as much as I can, and have brought the 100ms classRegister execution down to ~17ms. Unfortunately even 17ms isn't quite fast enough, so I'm going to look into replacing the current sysfs-based GPIO toggle method with an mmap method to speed up the synthetic clock.