This question already has an answer here:
I am new to C++ and Qt and I am having some trouble in achieving a good and not overcomplicated multithread communication environment.
Basically I have 3 threads, one for the GUI
, another for handling updates sent by a device
connected to the PC by USB, and another that will process the information acquired by the device
and control
the device
and the GUI
to change their state. So basically 3 threads: GUI
, device
and control
.
My fist approach was to have device
populate its private members with the information sent by USB and have some get()
methods that convert this data and return it (using mutexes to assure the data was still valid). The problem is when control
call the get()
methods in device
, it doesnt return anything new (I was expecting the methods to even never return since the thread is locked in another method, but they do return and with no new information, and also no breakpoints are triggered inside those get()
methods).
The usual way Qt does inter thread communications is by using Signal and Slots, but the problem with Signal and Slots is that when one thread is processing and it has a Slot, this Slot will never be executed if some Signal is sent. Even if I could manage to use Signal and Slots to trigger new data updates I am afraid there will be lots of Signals being sent since the device updates very fast, also I have lots of data types and using QAtomicInt will not be useful for many of them so my general question is which is the best way to make the threads share data and still keep running an infinite process loop?
A good example of my goal is having something like:
Control Thread:
while(true){
angle = device.getAngle(); //device is a member of control object and is running in a separate thread
doCalculations(angle);
}
Device Thread:
void process(){
while(true)
usbRead(data, size_of_data);
}
short getAngle(){
return (data[0] << 8 | data[1]);
}
I am not placing in this example the mutexes and etc, just a basic functionality to be expected.
As requested here is how I start my threads:
test::test(QWidget *parent) : QMainWindow(parent) , cvControl(device, 0)
{
//ui setup
connect(&device, SIGNAL(deviceConnected(bool)), this, SLOT(updateStatusConnection(bool)));
device.moveToThread(&deviceThread);
cvControl.moveToThread(&controlThread);
connect(&deviceThread, SIGNAL(started(void)), &device, SLOT(process(void)));
connect(&device, SIGNAL(deviceFinished(void)), &deviceThread, SLOT(quit(void)));
connect(&cvControl, SIGNAL(controlFinished(void)), &controlThread, SLOT(quit(void)));
connect(&deviceThread, SIGNAL(finished(void)), &device, SLOT(deleteLater(void)));
connect(&controlThread, SIGNAL(finished(void)), &cvControl, SLOT(deleteLater(void)));
connect(this, SIGNAL(startControlProcess(void)), &cvControl, SLOT(process(void)));
deviceThread.start();
controlThread.start();
}
void test::on_btnRun_clicked()
{
if(ui.btnRun->text() == "Run")
{
ui.btnRun->setText(QString("Stop"));
disbleControls();
emit startControlProcess();
}
else
{
ui.btnRun->setText(QString("Run"));
enableControls();
cvControl.abort.store(1);
}
}
Lets say your device works like this:
In this scenario there's no time for your to handle anything else in this thread, since this loop doesn't exit at all. However, you can change your loop to the following construct:
Or
Where
my_timer
is aQTimer*
and connected to the correct slot (in this casehandle_incoming_data()
. Note that the 0 msec timeout has a special meaning:For more information, see the section about
QTimer
.There are more than one approach for dealing with this issue:
1) Manual sleep and waking up at certain periods yourself to check if something is changed. This would be called polling by the jargon.
2) Use an event loop for your thread to process the events like signals and slots.
I would suggest the latter because the former may have the defect of literally not being able to do anything while sleeping and you may get it wrong. More importantly, you would also lose the great signal and slot mechanism all of a sudden, and your code would become somewhat more coupled than necessary.
As for doing the latter correctly, you would need to make sure to have the proper Qt event loop execution in place which is guaranteed by the later versions of
QThread
. That being said, please read the following documentation as I think it is useful to be aware of:You could also accomplish an in-between third solution as well, eventually, by mixing the two aforementioned approaches, namely: call the method below to explicitly make sure all the events are getting processed. It would be a semi-(a)sync way (depending on how you look at it) since you would need to do that when waking up from the sleeping for polling, but rather than dealing with the polling manually, you could use this convenience method.