Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Background thread won't react to signal until it is finished

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

Scheduled Pinned Locked Moved Unsolved General and Desktop
13 Posts 5 Posters 5.0k Views 4 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • V vlada

    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.

    kshegunovK Offline
    kshegunovK Offline
    kshegunov
    Moderators
    wrote on last edited by kshegunov
    #4

    @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));
    }
    

    Read and abide by the Qt Code of Conduct

    1 Reply Last reply
    2
    • Chris KawaC Online
      Chris KawaC Online
      Chris Kawa
      Lifetime Qt Champion
      wrote on last edited by
      #5

      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());
      }
      
      1 Reply Last reply
      2
      • V Offline
        V Offline
        vlada
        wrote on last edited by
        #6

        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.

        1 Reply Last reply
        0
        • V Offline
          V Offline
          vlada
          wrote on last edited by vlada
          #7

          Sorry to reopen this old thread but I still have some unsolved issues.

          I found one case where it would be very difficult and slow to implement the above suggestions. Would it be possible to tell the thread to pause a "for" or a "while" loop and check if didn't receive a cancel signal?

          Another problem I just discovered is that old threads are not deleted. Here is my code:

              QThread* searchThread = new QThread;
              FileSearch* search = new FileSearch(directories, 2); // 2 = get all files from subdirectories
              search->moveToThread(searchThread);
              connect(searchThread, SIGNAL(started()), search, SLOT(start()));
              connect(search, SIGNAL(fileCountUpdate(int)), this, SLOT(onFileCountUpdate(int)));
              connect(search, SIGNAL(filesFound(QStringList)), this, SLOT(onFilesFound(QStringList)));
              connect(this, SIGNAL(¨), search, SLOT(cancelScanning()));
              connect(search, SIGNAL(finished()), search, SLOT(deleteLater()));
              connect(searchThread, SIGNAL(finished()), searchThread, SLOT(deleteLater()));
              searchThread->start();
          

          If I run this thread a couple times and then emit the cancelScanning() signal, I can see it was called on all the previously run (and finished) threads except of the one which is currently running.

          My thread should find all files in a given path and all subdirectories. It works but it is sometimes difficult to cancel or takes a long time if there are many files or directories in one location.

          What I can not successfully solve is the situation when there is a search running (first of all I don't know how to detect this at all). What happens now is that if I start a new thread which finishes before the current one, I get response (return value emitted as a signal) from both threads. Instead of it, I would like to get response only from last one called. How could I achieve this? Thank you.

          kshegunovK 1 Reply Last reply
          0
          • K Offline
            K Offline
            karlheinzreichel
            wrote on last edited by
            #8

            You can use a QWaitCondition to break the thread when the thread is in a loop.
            See example : http://doc.qt.io/qt-5/qtcore-threads-waitconditions-example.html

            regards
            Karl-Heinz

            1 Reply Last reply
            0
            • V vlada

              Sorry to reopen this old thread but I still have some unsolved issues.

              I found one case where it would be very difficult and slow to implement the above suggestions. Would it be possible to tell the thread to pause a "for" or a "while" loop and check if didn't receive a cancel signal?

              Another problem I just discovered is that old threads are not deleted. Here is my code:

                  QThread* searchThread = new QThread;
                  FileSearch* search = new FileSearch(directories, 2); // 2 = get all files from subdirectories
                  search->moveToThread(searchThread);
                  connect(searchThread, SIGNAL(started()), search, SLOT(start()));
                  connect(search, SIGNAL(fileCountUpdate(int)), this, SLOT(onFileCountUpdate(int)));
                  connect(search, SIGNAL(filesFound(QStringList)), this, SLOT(onFilesFound(QStringList)));
                  connect(this, SIGNAL(¨), search, SLOT(cancelScanning()));
                  connect(search, SIGNAL(finished()), search, SLOT(deleteLater()));
                  connect(searchThread, SIGNAL(finished()), searchThread, SLOT(deleteLater()));
                  searchThread->start();
              

              If I run this thread a couple times and then emit the cancelScanning() signal, I can see it was called on all the previously run (and finished) threads except of the one which is currently running.

              My thread should find all files in a given path and all subdirectories. It works but it is sometimes difficult to cancel or takes a long time if there are many files or directories in one location.

              What I can not successfully solve is the situation when there is a search running (first of all I don't know how to detect this at all). What happens now is that if I start a new thread which finishes before the current one, I get response (return value emitted as a signal) from both threads. Instead of it, I would like to get response only from last one called. How could I achieve this? Thank you.

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #9

              @vlada said in Background thread won't react to signal until it is finished:

              Would it be possible to tell the thread to pause a "for" or a "while" loop and check if didn't receive a cancel signal?

              Nope. There are other options though - one is to batch up the data processing (as recommended above) - split the chunks of work through the event loop. Another option is to use an (atomic) flag or a volatile variable for that. E.g:

              class FileSearch : public QObject
              {
                  // ...
              private:
                  volatile bool scanning;
              };
              
              void FileSearch::start()
              {
                  // ...
                  scanning = true;
                  while ( scanning )  {
                       // ... do scanning or w/e
                  }
                  // ...
              }
              
              void FileSearch::cancelScanning()
              {
                  scanning = false;
              }
              

              and you connect that slot directly:

              connect(this, SIGNAL(...), search, SLOT(cancelScanning()), Qt::DirectConnection);
              

              What happens now is that if I start a new thread which finishes before the current one, I get response (return value emitted as a signal) from both threads. Instead of it, I would like to get response only from last one called. How could I achieve this?

              Cancel the current operation before starting another one (if I get the question correctly).

              PS.
              Unless you're stuck to Qt4 then you should migrate the signal-slot connections to the Qt5 syntax.

              Read and abide by the Qt Code of Conduct

              1 Reply Last reply
              1
              • Chris KawaC Online
                Chris KawaC Online
                Chris Kawa
                Lifetime Qt Champion
                wrote on last edited by
                #10

                @kshegunov

                volatile

                From the creator of C++:

                Do not use volatile except in low-level code that deals directly with hardware.
                Do not assume volatile has special meaning in the memory model. It does not. It is not -- as in some later languages -- a synchronization mechanism. To get synchronization, use atomic, a mutex, or a condition_variable.

                kshegunovK 1 Reply Last reply
                1
                • Chris KawaC Chris Kawa

                  @kshegunov

                  volatile

                  From the creator of C++:

                  Do not use volatile except in low-level code that deals directly with hardware.
                  Do not assume volatile has special meaning in the memory model. It does not. It is not -- as in some later languages -- a synchronization mechanism. To get synchronization, use atomic, a mutex, or a condition_variable.

                  kshegunovK Offline
                  kshegunovK Offline
                  kshegunov
                  Moderators
                  wrote on last edited by kshegunov
                  #11

                  Yeah, I know Chris, in this particular case the race is benign.

                  PS. And it's last on the list ... ;D

                  Read and abide by the Qt Code of Conduct

                  1 Reply Last reply
                  0
                  • dheerendraD Offline
                    dheerendraD Offline
                    dheerendra
                    Qt Champions 2022
                    wrote on last edited by dheerendra
                    #12

                    Signals/Slots communication across thread works through event loop. Inside the start() try to call processEvents. You start() function is busy & may not get time to process the slot().

                    :) So many responses. I did not see all them b4 posting my response.

                    Dheerendra
                    @Community Service
                    Certified Qt Specialist
                    http://www.pthinks.com

                    1 Reply Last reply
                    1
                    • V Offline
                      V Offline
                      vlada
                      wrote on last edited by
                      #13

                      I tried to call the processEvents() by a timer every 200ms or after processing 1000 items and that seems to do the trick.

                      Also I found out I was missing one more connection:

                      connect(search, SIGNAL(finished()), searchThread, SLOT(quit()));
                      

                      That was causing that my old threads were still alive even though the start() function already finished.

                      @kshegunov Thank you for the hint regarding new SLOT/SIGNAL syntax. I somehow didn't realize it has changed since the old one still works.

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved