QThreads and signal/slot connections : some signals are lost
-
Dear all,
I have a Renderer class (heriting QObject) which basically starts some heavy calculation when the slot startRender() is called, and emits every second a statusMessage(const QString &) signal to give some progress info, including the elapsed time in the form "HH:MM:SS elapsed"
I am running this class into a separate thread to keep the GUI responsive.Basically I am doing this inside my MainWindow when the user presses a Start button:
@ renderer = new Renderer();
QThread * mainThread = new QThread(this);
renderer->moveToThread(mainThread);connect(mainThread, SIGNAL(finished()), this, SLOT(rendererThreadFinished()));
connect(renderer, SIGNAL(statusOutput(const QString &)), this, SLOT(statusMessage(const QString &)));
connect(mainThread, SIGNAL(started()), renderer, SLOT(startRender()));//Let's rock
mainThread->start();
@When I test the application, it runs fine, except I am missing the four first status messages:
0.8% done, 00:00:05 elapsed
1.0% done, 00:00:06 elapsed
1.2% done, 00:00:07 elapsed
...If add a starter() signal to my MainWindow class and modify the code as following:
@ renderer = new Renderer(path);
QThread * mainThread = new QThread(this);
renderer->moveToThread(mainThread);connect(mainThread, SIGNAL(finished()), this, SLOT(rendererThreadFinished()));
connect(renderer, SIGNAL(statusOutput(const QString &)), this, SLOT(statusMessage(const QString &)));
//connect(mainThread, SIGNAL(started()), renderer, SLOT(startRender()));
connect(this, SIGNAL(starter()), renderer, SLOT(startRender()));//Let's rock
mainThread->start();
emit starter();
@Then it works fine:
0.2% done, 00:00:01 elapsed
0.3% done, 00:00:02 elapsed
0.5% done, 00:00:03 elapsed
0.7% done, 00:00:04 elapsed
0.8% done, 00:00:05 elapsed
1.0% done, 00:00:06 elapsed
1.2% done, 00:00:07 elapsed
...Okay this works, but to me it adds a useless starter() signal and is less intuitive. And I would like to understand what goes wrong here.
Any idea welcome!
Thanks,
Etienne
-
I think I started to understand.
If I replace:
@connect(mainThread, SIGNAL(started()), renderer, SLOT(startRender()));@by:
@connect(mainThread, SIGNAL(started()), renderer, SLOT(startRender()), Qt::QueuedConnection);@Then everything works.
So it seems that "QThread::started()" is emitted not from the thread the QObject lives in, but from the new thread. Which means it is not queued by default, and the renderer starts before the event loop is created (it seems that started() is emitted before exec())
Cheers,
Etienne
-
@
renderer->moveToThread(mainThread);
@should be placed just before:
@
mainThread->start();
@The situation is the following:
The QThread object instance lies in the thread that created the object.
The renderer object is created in the
If you move the Renderer object instance is created in the same thread, but if you move it to the thread managed by the QThread object's instance, then the connections between to and from the Renderer object should be done using either QueuedConnection or BlockingQueuedConnection. Auto and Direct connections cannot be used.Examine the following articles:
http://qt-project.org/forums/viewthread/20691/
http://qt-project.org/forums/viewthread/20738/ -
Thanks, this also solves the issue.
Any explanation about this? When it comes to threads, understanding is always better than experimenting...
So what is the difference between:
moveToThread then connect then start
and
connect then moveToThread then start ?Etienne
-
You could debug Qt's source code and find the difference - I don't know it.
But surely enough, if the scenario where setting up the connections and then moving the object to the thread works, then Qt framework automatically does the "queue"-related work and it surely better.
-
I guess the difference is that if you first connect and then moveToThread the connectionType choosen by Qt (if you don't set it explicitly) will be DirectConnection which is incorrect after the moveToThread. This might mess up the connections.
If you first moveToThread and then connect the correct QueuedConnection type is choosen by default.