Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

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 these QObject subclasses to their respective QThread and then I start the thread.

    The function void Master::startWorker() is called during the paintEvent() of the QWidget. 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 using Qt::QueuedConnection would mean that my main GUI thread will not stop, also I tried the with Qt::AutoConnection everywhere and the results were the same.


  • Lifetime Qt Champion

    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.


  • Moderators

    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();
        // ...
    }
    

  • Lifetime Qt Champion

    @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 the startWorking() 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's startWorking() 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 with QMetaObject::invokeMethod().


  • Moderators

    Seems correct. As for your reasoning, it does appear to me your work type succumbs best to pooling the threads and running the calculation as jobs. Like QtConcurrent::run or alike.



  • @kshegunov Thanks! Yeah, I tried QtConcurrent::run as well first. 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). 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 using QThread. If you have any references, I would be happy to know :)


  • Moderators

    @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 from QThread override run 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 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?

    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 from QThread override run 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 use moveToThread() instead of subclassing QThread and implementing its run 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 :)


  • Moderators

    @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.


Log in to reply