Background thread won't react to signal until it is finished



  • Hi,

    I have a problem with signal for a background thread. I create it like this:

            QThread* newThread = new QThread;
            MyClass* thread = new MyClass(parameters);
            thread->moveToThread(newThread);
            connect(newThread, SIGNAL(started()), thread, SLOT(start()));
            connect(thread, SIGNAL(progress(int)), this, SLOT(onProgress(int)));
            connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
            connect(myThread, SIGNAL(finished()), myThread, SLOT(deleteLater()));
            connect(this, SIGNAL(mySignal()), thread, SLOT(onSignal()));
            thread->start();
    

    I have no problems receiving the progress from the thread. But when I emit "mySignal", nothing happens until the thread finishes. Then the onSignal() function is executed as many times as I emited the signal.

    What is even more strange, the function is even executed after the thread finished and the whole instance should be deleted. Or at least I would expect it.

    What did I wrong? How can I change something in the background thread?


  • Moderators

    The naming you chose is mighty confusing ;) Even you got caught, so you have newThread, thread, myThread and then you start thread while I'm guessing you really start newThread and myThread should be thread. The usual convention is to call these entities thread and worker. Easier to navigate ;)

    Anyway... When the thread (newThread in your sample) starts it spawns an event loop that processes incoming signals. From your description it sounds like you're blocking that loop somehow, and only when you're done blocking all the signals that were queued are processed.

    It's guessing on my part without seeing more code but I'm thinking you're starting some sort of loop in start() slot? Generally you shouldn't because there's event loop running already in that thread.



  • I'll try to fix my sample code so that it is less confusing. I renamed the instances and did not paste the real code. But I apparently did it wrong.

    QThread* thread = new QThread;
    MyClass* worker = new MyClass(parameters);
    worker->moveToThread(thread);
    connect(thread, SIGNAL(started()), worker, SLOT(start()));
    connect(worker, SIGNAL(progress(int)), this, SLOT(onProgress(int)));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(this, SIGNAL(mySignal()), worker, SLOT(onSignal()));
    thread->start();
    

    In the worker class there is a function:

    void MyClass::start()  {
    for (int i=0; i<something;i++) {
        slowFunction(i);
        }
    }
    

    I get this construction from some Internet tutorial or guide. It used to work correctly until I noticed this problem.

    Is this wrong? Is there another way how the thread should be started? Thank you.


  • Qt Champions 2016

    @vlada said:

    In the worker class there is a function

    That'll do it. You're blocking the event loop, as @Chris-Kawa correctly guessed. :)
    You must have a running event loop (unblocked) so the slot invocation events are processed. My advice is to run this particular for loop through the event queue, for example:

    class MyClass : public QObject
    {
        // ...
        Q_INVOKABLE void slowFunction(int);
    }
    
    void MyClass::start()
    {
        const int startIndex = 0;
        QMetaObject::invokeMethod(this, "slowFunction", Qt::QueuedConnection, Q_ARG(int, startIndex));
    }
    
    void MyClass::slowFunction(int i)
    {
        if (i >= something)
            return;
    
        // Do the slow thing
    
        // Queue the function for the next iteration
        i++;
        QMetaObject::invokeMethod(this, "slowFunction", Qt::QueuedConnection, Q_ARG(int, i));
    }
    

  • Moderators

    What @kshegunov said or use a 0-timeout timer as a loop:

    void MyClass::start()  {
       startTimer(0);
    }
    
    void MyClass::timerEvent(QTimerEvent* e) {
       if (something--)
          slowFunction();
       else
          killTimer(e->timerId());
    }
    


  • Thanks a lot for your suggestions. I fixed my code and now it works correctly. Unfortunately it is a little slower then it used to be. But that is not a big problem.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.