3

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);
    }
}
László Papp
  • 51,870
  • 39
  • 111
  • 135
Michel Feinstein
  • 13,416
  • 16
  • 91
  • 173
  • Instead of the infinite loop, you could call each iteration by a `QTimer`. This gives you enough time inbetween for signals and slots. – Zeta Dec 29 '13 at 22:26
  • Yes but this sounds complicating for me, for example, `device` has 18 parameters being updated and most of the time only 2 or 3 are needed so everytime there is new data sending a Signal to `control` will the data seems to me to too much data flux...what do you think? – Michel Feinstein Dec 29 '13 at 22:30
  • You already asked it here, and we responded: http://stackoverflow.com/questions/20825039/qthread-slots-behavior Please reformulate your question by referring to existing threads, etc. It seems you are trying to solve one task by several small threads, and occasional readers do not know the other parts. Either way, what is wrong about the answer over there? You could execute an event loop as written, and it will schedule the events for processing. Also, I would suggest to read about these things a bit more before posting questions every hour or second. – László Papp Dec 29 '13 at 22:31
  • Laszlo, please read the question carefully, the link you posted is a question about "what happens if..." this question is "what is the recommended way to do things", I am looking for recommendations for the design of the general arquitecture of the program, not asking a specific question about Qt. – Michel Feinstein Dec 29 '13 at 22:33
  • There is nothing wrong with the answer over there I just want to see if there are other ways. I am not familiar with event loops and how they work, if they depend in signal and slots, as I said things can get complicated with so many data being sent and received. If you think thats the best thing to do, could you please send me some further information about how to do it? I edited the OP to include an example of what is my goal. – Michel Feinstein Dec 29 '13 at 22:42
  • @mFeinstein: I do not see any example included in your post. – László Papp Dec 29 '13 at 22:48
  • I just added them, it took me a while to keep it short and straingt to the point. – Michel Feinstein Dec 29 '13 at 22:51
  • @mFeinstein: " I am not familiar with event loops and how they work, if they depend in signal and slots, as I said things can get complicated with so many data being sent and received" -> what do you mean by that? What complication? – László Papp Dec 29 '13 at 22:54
  • when you say "event loops" are you refering to QEventLoop right? So I am not familiar with that calss yet, how it works and etc...if it works by filtering signals and slots, things can get complicated because I have too many data being updated and most of the time the program doesnt need all that data, so I want to avoid sending signals everywhere with lots of data triggering different slots all the time and maybe having old data to be processed because the signal is queued. – Michel Feinstein Dec 29 '13 at 22:58
  • @mFeinstein: so what? Signals and slots can have parameters as well, like a boolean "process_it_now", surely, you can filter out what you want to process, and what not. – László Papp Dec 29 '13 at 23:00
  • Yes, but what about "give_me_the_data(angle1)"? I will have to ask for a data, sleep and wait for a reply in another method? this sounds complicated :/ – Michel Feinstein Dec 29 '13 at 23:03
  • Your example does not make sense. For instance this part: `void getAngle(){ return (data[0] << 8 | data[1]); }` Returning a value in a void retval function? Then you have separate process and processAngle. It is a mess. :) Please clean it up... Moreover, you do not have any sleep in the code, so I am not sure what you are talking about with regards to that. I am not even convinced at this point you understand what **you** want. – László Papp Dec 29 '13 at 23:14
  • I am just saying if I implement a system where I have to ask things by message, I will have to sleep to wait the message to return with my data. I wanted something more clean and quick as the example. – Michel Feinstein Dec 29 '13 at 23:18
  • I really cannot get your problem with the qt event loop. It feels like a premature optimization, but here you go: the alternative is to sleep, and wake up periodically for manual polling, but then you have to forget qt signals and slots, and you are on your own, and you will /not/ be able to process correctly during the sleep, or alternatively: you will reinvent the qt event loop on your own, or probably some less robust and stable version of it... – László Papp Dec 29 '13 at 23:19
  • I dont have any problems with it! I just dont know how it works so I cant have an opinion about it, thats all, I am reading about it now and I like to understand things before I accept them. – Michel Feinstein Dec 29 '13 at 23:21
  • what about `QCoreApplication::processEvents()`? – Michel Feinstein Dec 29 '13 at 23:24
  • That is the reason why I mentioned do not post questions for the same task every 2nd or 3d hour. Try to investigate more about the information you get. Here it goes a good bit of it: http://doc-snapshot.qt-project.org/qdoc/qthread.html#exec and http://doc-snapshot.qt-project.org/qdoc/qcoreapplication.html#processEvents – László Papp Dec 29 '13 at 23:26
  • @mFeinstein: I wrote that long ago to you, but you disregarded that, too, like in general. :/ – László Papp Dec 29 '13 at 23:29
  • I am investigating but there are 1000 solutions to this problem I wanted to see whats people general opinions and recommendations so I can avoid pitfalls – Michel Feinstein Dec 29 '13 at 23:29
  • So basically your suggestion is to call `exec()` and wait it returns? – Michel Feinstein Dec 29 '13 at 23:30

2 Answers2

2

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:

int QThread::exec() [protected]

Enters the event loop and waits until exit() is called, returning the value that was passed to exit(). The value returned is 0 if exit() is called via quit().

This function is meant to be called from within run(). It is necessary to call this function to start event handling.

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.

void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents) [static]

Processes all pending events for the calling thread according to the specified flags until there are no more events to process.

You can call this function occasionally when your program is busy performing a long operation (e.g. copying a file).

In the event that you are running a local loop which calls this function continuously, without an event loop, the DeferredDelete events will not be processed. This can affect the behaviour of widgets, e.g. QToolTip, that rely on DeferredDelete events to function properly. An alternative would be to call sendPostedEvents() from within that local loop.

Calling this function processes events only for the calling thread.

László Papp
  • 51,870
  • 39
  • 111
  • 135
  • Ok, I just have some questions about the implementation of this: 1- what happens if I call `QCoreApplication::processEvents()` and there are multiple signals being sent...can this call never return since the signal queue will never be empty (the time to execute a Slot is sufficient to arrive other signals)? And 2-I am sorry but I cant understand what this `exec()` call does exactly, who calls it? It some funciton I have to subclass? Who calls exit? What does it do? I am sorry but this documentation doesnt show me how to really use it, good tutorials examples are most welcome – Michel Feinstein Dec 29 '13 at 23:50
  • I read and I didnt understand the general `exec()` behaviour. I can see every QThread has an event loop, and `exec()` starts it, so if I am running inside this thread, and only one method can run in a thread at a time, calling exec() will stop my current `process()` method? And I am not subclassing `run()` so who calls `run()`? thats why I asked for examples, the general behaviour of things are not clear to me – Michel Feinstein Dec 30 '13 at 00:01
  • I dont intent to implement my own logic, I want to use Qt framework to do the job for me. I am not doing anything creepy, so I believe I have `exec()` running just fine...but the slots will never be executed because `process()` doesnt return, right? – Michel Feinstein Dec 30 '13 at 00:04
  • I thought you meant by creepy like subclassing `run()` making it unable to call `exec()` – Michel Feinstein Dec 30 '13 at 00:10
  • I now understand your general answer, you are saying use Signal and Slots for everything, and dont have a `process()` infinite lopp, just let the Slots be called whenever they are needed to process something...thats fine...but arent any other ways? As I said in my question I wanted to avoid this, I wanted something more simple as my small example, just calling a method to return one data when I need this data, and not having a slot being called every single time there is new data available – Michel Feinstein Dec 30 '13 at 00:15
  • And I dont understad why dont you spend more time explaining and proving than complaining – Michel Feinstein Dec 30 '13 at 00:47
  • sorry, I just saw this one was closed but I didnt see you still has to mark an answer, even in a closed question...my mistake – Michel Feinstein Jan 06 '14 at 22:12
1

Lets say your device works like this:

while(keep_running){
    handle_incoming_data();
}

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:

public slots:
    void single_step(){
        if(keep_running){
            handle_incoming_data();
            QTimer::singleShot(0, this, SLOT(single_step()));
        }
    }

Or

public slots:
    void start_work(){
        my_timer->start(0);
    }

    void stop_work(){
        my_timer->stop();
    }

Where my_timer is a QTimer* and connected to the correct slot (in this case handle_incoming_data(). Note that the 0 msec timeout has a special meaning:

As a special case, a QTimer with a timeout of 0 will time out as soon as all the events in the window system's event queue have been processed. This can be used to do heavy work while providing a snappy user interface:

QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing()));
timer->start();

For more information, see the section about QTimer.

Community
  • 1
  • 1
Zeta
  • 103,620
  • 13
  • 194
  • 236
  • The OP cannot do this without an event loop in the receiver's thread in which case the QTimer becomes moot an approach. Essentially, because the QTimer would also need an event loop, but then there is no point to it. :) Please check the answer I linked. It is explained in there. – László Papp Dec 29 '13 at 22:37
  • @LaszloPapp: It's probably not the best way, however, as long as we know _nothing_ about OP's threads it could be a valid solution ;). I know about the receiver event loop, this was a thing that took a little bit of coffee to get right the first time. – Zeta Dec 29 '13 at 22:39
  • See? :) That is why I mentioned to him to post references to his earlier threads. I was following them closely, and holding the hands. :) For some reason, he does not wanna do that as you can see, hence the confusion. – László Papp Dec 29 '13 at 22:40
  • Laslo, my threads have more then 100 lines of code so far, I cant make them short and if they are big people will complain...I added a small example of my goal. – Michel Feinstein Dec 29 '13 at 22:44
  • Zeta, also be aware that the OP has a tendency of rejecting good practices, and then saying the program was rewritten (like here: http://stackoverflow.com/questions/20808217/qthread-weird-behaviour/20810629#20810629); just saying. :) – László Papp Dec 29 '13 at 22:47
  • Laszlo, this kind of comment wont help anyone, the one thing it can do is to make people less prone to help me. If you dont want to help, its fine, its your choice, just please dont disturb. – Michel Feinstein Dec 29 '13 at 22:54
  • Zeta: the problem is that with your answer AFAICT, as written before, QTimer needs an event loop, which he does not have, and does not seem to want for some reason in that thread. In which case, your recommendation is not functional IMHO. – László Papp Dec 29 '13 at 23:01
  • Laszlo I never said "I dont want it" I just said I dont know anything about it, its a big difference. So I was asking you questions about its behaviour, thats all. – Michel Feinstein Dec 29 '13 at 23:11
  • @mFeinstein: It is not accidental or inconsiderate what I wrote in the other thread! – László Papp Dec 29 '13 at 23:12
  • about what do you mean? – Michel Feinstein Dec 29 '13 at 23:14
  • @mFeinstein: that you should use an event loop IMO as written before. The way I see it, we are just discussing it a lot, and getting back to the same common denominator. – László Papp Dec 29 '13 at 23:16
  • I am reading about it and I like to see other peoples opinion, but you usually spend half your answers saying how bad I do things and just drives people away and make the post HUGE with comments...and later you say its my fault. I really appreciate your help Laszlo, but our communications are not being good usually. Please keep on the subject, reply in an answer and explain the solution, just like @Zeta kindly did. – Michel Feinstein Dec 29 '13 at 23:27
  • @mFeinstein: I would really like to provide an example, however I don't have Qt installed at my machine and it would take quite a while. Hopefully your problem has been solved before that. By the way, it helps a little bit more if you show your actual (minimal example) class you use for the driver. – Zeta Dec 29 '13 at 23:29
  • what driver? The USB driver? – Michel Feinstein Dec 29 '13 at 23:32
  • @mFeinstein: Let me formulate this differently: how do you start the USB driver thread? You should add that to your question, so that other can see what Qt facilities are possible. – Zeta Dec 29 '13 at 23:35
  • Zeta, he is using QThread the right way after he got pointed to the right blog post from the author, i.e. he is not subclassing QThread. – László Papp Dec 29 '13 at 23:41
  • Zeta, he pasted almost same code as 4-5 Q/A threads before. He is still asking almost the same questions all over again. I am off, I am afraid. Good luck to helping here, but my recommendation is value your free time. :-) – László Papp Dec 30 '13 at 00:06