Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection
-
Hi, I am using worker threads to do some calculations. In my main GUI thread, I have a function that is called to invoke a slot in a worker thread. The worker thread then invokes multiple slots in subworker threads. The entire process is something like this:
Master GUI Thread (subclass of
QWidget
)void Master::startWorker() { QMetaObject::invokeMethod(workerObject, "startWorking", Qt::QueuedConnection, Q_ARG(double, wid), Q_ARG(double, hit)); }
Worker Object (subclass of
QObject
)void Worker::startWorking(double wid, double hit) { for(int ii = 0; ii < subWorkerCount; ++ii) { QMetaObject::invokeMethod(subWorkerObject[ii], "startSubWorking", Qt::QueuedConnection, Q_ARG(double, wid), Q_ARG(double, hit)); } }
SubWorker Object (subclass of
QObject
)void SubWorker::startSubWorking(double wid, double hit) { // Does the calculation: total time ~15 ms }
All the worker and subworkers have their own threads and I use
moveToThread()
to move theseQObject
subclasses to their respectiveQThread
and then I start the thread.The function
void Master::startWorker()
is called during thepaintEvent()
of theQWidget
. If I do not call this function during the paintEvent() then the total time taken by the paintEvent() is less than 20 ms. But, if I call this function then the total time taken by paint event is around 170 ms. Why is this the case? I thought usingQt::QueuedConnection
would mean that my main GUI thread will not stop, also I tried the withQt::AutoConnection
everywhere and the results were the same. -
Why do you use QMetaObject::invokeMethod() instead simple signal/slot connections? Then you don't need to worry about Queued/Direct connections at all.
When the gui thread freezes you do something wrong. But without a simple reproducer we can't tell why. Output the current thread id to see in which thread the operations are done.
-
@Christian-Ehrlicher said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
Why do you use QMetaObject::invokeMethod() instead simple signal/slot connections? Then you don't need to worry about Queued/Direct connections at all.
This is because in my application the Worker and SubWorker objects are created dynamically and I need to pass valuess to them in a slot so that they can start working. Qt doesn't allow me to create vectors of signals and so I need to use
QMetaObject::invokeMethod()
function.When the gui thread freezes you do something wrong. But without a simple reproducer we can't tell why. Output the current thread id to see in which thread the operations are done.
I did that, all the SubWorker threads are done before the GUI thread is done. It seems the GUI thread just stops till the worker threads are done. The function
void Master::startWorker()
is the only way I invoke the slot in my Worker object during the entire update process. I am new to multi-threading and so I thought maybe I am missing something simple, I will make a small reproducible code and share it here. -
What @Christian-Ehrlicher said, and I specifically want to know the result of:
void SubWorker::startSubWorking(double wid, double hit) { Q_ASSERT(thread() == QThread::currentThread()); qDebug() << QThread::currentThread(); // ... }
coupled together with:
void Worker::startWorking(double wid, double hit) { Q_ASSERT(thread() == QThread::currentThread()); qDebug() << QThread::currentThread() << qApp->thread(); // ... }
-
@CJha said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
Qt doesn't allow me to create vectors of signals and so I need to use QMetaObject::invokeMethod() function.
I don't see a difference...
all the SubWorker threads are done before the GUI thread is done
I asked you to print out thread thread id...
-
@kshegunov Thanks!
@kshegunov @Christian-Ehrlicher This is the result:Thread: QThread(0x2858b65a0e0) qApp Thread: QThread(0x2858614f570) // For Worker Thread: QThread(0x2858b659960) // For SubWorker Thread: QThread(0x2858b659cd0) // For SubWorker Thread: QThread(0x2858b65a040) // For SubWorker Thread: QThread(0x2858b65a270) // For SubWorker Thread: QThread(0x2858b659c30) // For SubWorker Thread: QThread(0x2858b65a4f0) // For SubWorker Thread: QThread(0x2858b659ff0) // For SubWorker Thread: QThread(0x2858b659d20) // For SubWorker Thread: QThread(0x2858b659f50) // For SubWorker Thread: QThread(0x2858b659c80) // For SubWorker
I don't see a difference...
My Worker objects are created during run time, their numbers can vary. I need to pass data through the
startWorking()
slot to my worker (so that the connection is queued and thestartWorking()
is executed in the worker's thread). For each worker the data that is passed is different, this means that I need a unique signal in my master for each worker'sstartWorking()
slot. If I use the same signal from my master then each time I will send a data it will be received by all the workers and not only to the one it is intended for. Since workers are created dynamically during run-time and signals cannot be created at run-time (as far as I know), I cannot use signals/slots to do this communication and so I am stuck withQMetaObject::invokeMethod()
. -
@kshegunov Thanks! Yeah, I tried
QtConcurrent::run
as well first. The problem withQtConcurrent::run
is that it doesn't allow me to change the priority of the thread pool (there are some workarounds but I am not really convinced). And I really need to have a thread priority system as I have to deal with high-frequency data generated with data acquisition systems (DAQs) using these classes. I could not find any other proper solution so I decided to make my own usingQThread
. If you have any references, I would be happy to know :) -
@CJha said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
The problem with QtConcurrent::run is that it doesn't allow me to change the priority of the thread pool (there are some workarounds but I am not really convinced).
You shouldn't need to. Changing the thread priority won't give you any performance as such.
@CJha said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
And I really need to have a thread priority system as I have to deal with high-frequency data generated with data acquisition systems (DAQs) using these classes.
If you want to enforce the latency constraint, then you should go low-level with a producer-consumer queue, not rely on the mutex of the signal-slot mechanism, much less resolve the slot calls at runtime (i.e.
invokeMethod
with a string argument). Search the forums I've written at least 2-3 posts how a TS queue can be straightforwardly implemented. After you get that, derive fromQThread
overriderun
and put your calculations there dropping the worker object altogether.Note: When spinning the worker threads you want to use exactly
QThread::idealThreadCount
not more, not less. Switching between threads can be really costly. -
@kshegunov said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
You shouldn't need to. Changing the thread priority won't give you any performance as such.
I thought if I put the priority as
QThread::LowPriority
for one instance andQThread::HighPriority
for the other then Qt will make sure that one with higher priority gets the resources whenever it requires regardless of how long the threads with lower priority are waiting? If this is not the situation then why do we have different priorities in the first place?If you want to enforce the latency constraint, then you should go low-level with a producer-consumer queue, not rely on the mutex of the signal-slot mechanism, much less resolve the slot calls at runtime (i.e.
invokeMethod
with a string argument). Search the forums I've written at least 2-3 posts how a TS queue can be straightforwardly implemented. After you get that, derive fromQThread
overriderun
and put your calculations there dropping the worker object altogether.Note: When spinning the worker threads you want to use exactly
QThread::idealThreadCount
not more, not less. Switching between threads can be really costly.Most of the people on forums and blogs suggest subclassing
QObject
and usemoveToThread()
instead of subclassingQThread
and implementing itsrun
method, why do you suggest differently? Is there a catch?
Never heard of TS queue, I will try to learn about it. Thanks for the help :) -
@CJha said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
I thought if I put the priority as QThread::LowPriority for one instance and QThread::HighPriority for the other then Qt will make sure that one with higher priority gets the resources whenever it requires regardless of how long the threads with lower priority are waiting? If this is not the situation then why do we have different priorities in the first place?
This is not Qt-specific. On a multitasking system the priority of process or thread determines the timeslices it gets from the OS's scheduler. However, there are so many cores on a system, you can't get performance out of nothing. That's why if you're going to crunch numbers, you want to have a number of threads equal to the number of cores, so each thread runs on its own core as much of the time as possible without being interrupted. Having more threads than cores, contrary to (the layman) popular belief, doesn't produce better performance, it can get you some latency benefit if the cores are not busy, otherwise it's an overhead for the scheduler to slice and rebalance the time slots. That's why you have priorities - to "hijack" latency from other threads/processes running on that system, but again this doesn't mean your code is going to be running faster.
@CJha said in Main GUI thread stops when worker thread is invoked using Qt::QueuedConnection:
Most of the people on forums and blogs suggest subclassing QObject and use moveToThread() instead of subclassing QThread and implementing its run method, why do you suggest differently? Is there a catch?
Yes, that's correct. For the general case this is best. If you want to squeeze out the most of the hardware however you're going to want to remove the pesky overhead that comes with this. That's "the catch" - having/choosing the correct tool for the job at hand.
Never heard of TS queue, I will try to learn about it.
That's a "thread-safe queue" so you don't get the wrong idea.