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. QThread and EventLoop: how can I wait for all the events (workers) to be done?
Qt 6.11 is out! See what's new in the release blog

QThread and EventLoop: how can I wait for all the events (workers) to be done?

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 7 Posters 13.1k Views 2 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.
  • mbruelM Offline
    mbruelM Offline
    mbruel
    wrote on last edited by
    #1

    Hi,
    I've a working thread where I move some workers.
    I need to be able to wait that all of them are processed.
    How can I get the event loop of the working thread? Is there an easy way to wait until all its events are processed? Is there a limit on the size of the event loop?
    Cheers

    jsulmJ 1 Reply Last reply
    0
    • mbruelM mbruel

      Hi,
      I've a working thread where I move some workers.
      I need to be able to wait that all of them are processed.
      How can I get the event loop of the working thread? Is there an easy way to wait until all its events are processed? Is there a limit on the size of the event loop?
      Cheers

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote on last edited by
      #2

      @mbruel said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

      I need to be able to wait that all of them are processed.

      Why do you want to wait? Waiting in an event driven applications is a bad idea.
      Why not letting your main thread know that everything was processed?
      So, if your worker thread finished all jobs it can emit a signal to notify the main thread.

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      1 Reply Last reply
      1
      • mbruelM Offline
        mbruelM Offline
        mbruel
        wrote on last edited by
        #3

        Well I'm using a special taskThread where I move Tasks that will just run one slot when they will be triggered from another thread ( not the gui one). After running that slots they will trigger their own destruction (deleteLater)

        So basically from my simulationThread, I'm creating Tasks in the heap, that I move to taskThread. I don't store a list of that Tasks in taskThread as I didn't see the interest but maybe that is what I could do.

        At one point I want to wait that all that Tasks have been processed. I know that they should all have been triggered and thus that there should be events in the EventLoop of the taskThread. I was thinking there might be a way to purge (wait for all the events are done) this EventLoop.

        Is the use case clear?

        I suppose I could store a list of all my Taks and make sure they are all processed but this means I will end up with an active while loop on the size of that list. This is not really pretty... Is there another way I could achieve this? using the EventLoop or not?

        KroMignonK 1 Reply Last reply
        0
        • Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #4

          This task looks more like something for QtConcurrent.

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          mbruelM 1 Reply Last reply
          0
          • mbruelM mbruel

            Well I'm using a special taskThread where I move Tasks that will just run one slot when they will be triggered from another thread ( not the gui one). After running that slots they will trigger their own destruction (deleteLater)

            So basically from my simulationThread, I'm creating Tasks in the heap, that I move to taskThread. I don't store a list of that Tasks in taskThread as I didn't see the interest but maybe that is what I could do.

            At one point I want to wait that all that Tasks have been processed. I know that they should all have been triggered and thus that there should be events in the EventLoop of the taskThread. I was thinking there might be a way to purge (wait for all the events are done) this EventLoop.

            Is the use case clear?

            I suppose I could store a list of all my Taks and make sure they are all processed but this means I will end up with an active while loop on the size of that list. This is not really pretty... Is there another way I could achieve this? using the EventLoop or not?

            KroMignonK Offline
            KroMignonK Offline
            KroMignon
            wrote on last edited by KroMignon
            #5

            @mbruel said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

            I was thinking there might be a way to purge (wait for all the events are done) this EventLoop.

            You can check if any pending event exists with QThread::currentThread()->eventDispatcher()->hasPendingEvents().
            I think this what you are looking for.

            You can check this at the end of the slot process and raise a signal to inform about empty event list.

            It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

            1 Reply Last reply
            1
            • Christian EhrlicherC Christian Ehrlicher

              This task looks more like something for QtConcurrent.

              mbruelM Offline
              mbruelM Offline
              mbruel
              wrote on last edited by mbruel
              #6

              @Christian-Ehrlicher said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

              This task looks more like something for QtConcurrent.

              Well I've done this design cause I've already 2 ThreadPools for more heavy Computation tasks and I didn't want to create another one. Plus those specific Tasks will be triggered by a signal and I think the Threads of a ThreadPool don't have their event loop started. Am I wrong?

              @KroMignon said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

              You can check if any pending event exists with QThread::currentThread()->eventDispatcher()->hasPendingEvents().
              I think this what you are looking for.
              You can check this at the end of the slot process and raise a signal to inform about empty event list.

              Yes thanks, this could be what I'm looking for.
              I guess instead of a signal I will rather use a QWaitCondition that I would wake at the end of the processing slot each time hasPendingEvents is empty.

              1 Reply Last reply
              0
              • mbruelM Offline
                mbruelM Offline
                mbruel
                wrote on last edited by mbruel
                #7

                QAbstractEventDispatcher::hasPendingEvents is marked as obsolete.
                What shall I use instead to achieve the same goal?

                Another question: will hasPendingEvents return true when I'm processing the last event (I mean inside the slot of the last event) or will it return 1?

                1 Reply Last reply
                0
                • fcarneyF Offline
                  fcarneyF Offline
                  fcarney
                  wrote on last edited by fcarney
                  #8

                  @mbruel said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                  I need to be able to wait that all of them are processed.

                  Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                  Edit: Maybe counter is in main thread and signals decrement on thread completion.
                  Edit2: The last thread decrement signal would also check the counter is zero and emit another signal/call a function for what has to be done now that all threads are done.

                  C++ is a perfectly valid school of magic.

                  kshegunovK mbruelM 2 Replies Last reply
                  0
                  • Kent-DorfmanK Offline
                    Kent-DorfmanK Offline
                    Kent-Dorfman
                    wrote on last edited by
                    #9

                    @fcarney said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                    Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                    Yes. A semaphore can be used for this purpose.

                    The dystopian literature that served as a warning in my youth has become an instruction manual in my elder years.

                    1 Reply Last reply
                    1
                    • fcarneyF fcarney

                      @mbruel said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                      I need to be able to wait that all of them are processed.

                      Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                      Edit: Maybe counter is in main thread and signals decrement on thread completion.
                      Edit2: The last thread decrement signal would also check the counter is zero and emit another signal/call a function for what has to be done now that all threads are done.

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

                      @fcarney said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                      Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                      Yes, and a pretty simple one:

                      class SomeClass
                      {
                          int processedTasks;
                      };
                      
                      void SomeClass::someMethod()
                      {
                          QObject * worker = new ...;
                          QObject::connect(worker, &QObject::destroyed, this, [this] () -> void {
                              processedTasks--;
                              if (processedTasks == 0)
                                  emit finishedJob();
                          });
                      
                          processedTasks++;
                          worker->moveToThread(...);    
                      }
                      

                      @Kent-Dorfman said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                      Yes. A semaphore can be used for this purpose.

                      That's an overkill. If you're only counting, then it's atomic integers, you don't need blocking nor the complexity of a semaphore.

                      Read and abide by the Qt Code of Conduct

                      Kent-DorfmanK 1 Reply Last reply
                      2
                      • kshegunovK kshegunov

                        @fcarney said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                        Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                        Yes, and a pretty simple one:

                        class SomeClass
                        {
                            int processedTasks;
                        };
                        
                        void SomeClass::someMethod()
                        {
                            QObject * worker = new ...;
                            QObject::connect(worker, &QObject::destroyed, this, [this] () -> void {
                                processedTasks--;
                                if (processedTasks == 0)
                                    emit finishedJob();
                            });
                        
                            processedTasks++;
                            worker->moveToThread(...);    
                        }
                        

                        @Kent-Dorfman said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                        Yes. A semaphore can be used for this purpose.

                        That's an overkill. If you're only counting, then it's atomic integers, you don't need blocking nor the complexity of a semaphore.

                        Kent-DorfmanK Offline
                        Kent-DorfmanK Offline
                        Kent-Dorfman
                        wrote on last edited by Kent-Dorfman
                        #11

                        @kshegunov said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                        That's an overkill. If you're only counting, then it's atomic integers, you don't need blocking nor the complexity of a semaphore.

                        What complexity? You don't HAVE to use the semaphore for blocking, and its access is guaranteed to be atomic. Simply initialize a counting semaphore with the max number of allowed threads and decrement when they are started, and increment when they complete.

                        IMHO, either approach is equally valid. Just depends on your particular bias. I prefer the semaphore because it has the side-effect of being a lock if you want it to be.

                        The dystopian literature that served as a warning in my youth has become an instruction manual in my elder years.

                        1 Reply Last reply
                        0
                        • fcarneyF fcarney

                          @mbruel said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                          I need to be able to wait that all of them are processed.

                          Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                          Edit: Maybe counter is in main thread and signals decrement on thread completion.
                          Edit2: The last thread decrement signal would also check the counter is zero and emit another signal/call a function for what has to be done now that all threads are done.

                          mbruelM Offline
                          mbruelM Offline
                          mbruel
                          wrote on last edited by mbruel
                          #12

                          @fcarney said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                          Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                          Hello,
                          What I need to do is to wait that the event queue of my _eventThread is consumed. There is only one Thread where several hundreds workers have been moved and are supposed to be triggered.
                          The thing is that I want to wait that first they are all started and then after that they are finished. The issue I have is to make sure they are all started. They will be started sequentially in the event loop of _eventThread. That is why I wanted to be able to interact directly on the event loop to know how many pending event it has and when it will have processed all. (knowing that no new events will be generated)

                          For now, this what I've done:
                          I'm holding a list of all the pending event Tasks. In my waitForDone method, I use a WaitCondition that is waken when an event Task finishes if the list is empty.
                          It kind of seem to work.
                          Any comments / advices?

                          class LazyComputationManager
                          {
                          private:
                             QThreadPool             _threadPool;
                             
                             QThread                 _eventThread;
                             QList<ComputationTask*> _pendingEventTasks;
                             QWaitCondition          _waitPendingEvents;
                             QMutex                  _securePendingEvents;
                             
                          public:
                          
                             ComputationTask *_appendEventTask(ComputationTask *task)
                             {
                                 task->moveToThread(&_eventThread);
                                 connect(task, &ComputationTask::eventTaskDone, this, &LazyComputationManager::handleEventTaskDone);
                          
                                 QMutexLocker lockEventThread(&_securePendingEvents);
                                 _pendingEventTasks.append(task);
                                 return task;
                             }
                          
                             void handleEventTaskDone(ComputationTask *task)
                             {
                                 QMutexLocker lockEventThread(&_securePendingEvents);
                                 _pendingEventTasks.removeOne(task);
                          
                                 if (_pendingEventTasks.isEmpty())
                                     _waitPendingEvents.wakeAll();
                          
                                 emit task->readyForDestruction(); // this trigger the destruction of the task (deleteLater)
                             }
                          
                             void waitForDone()
                             {
                                 // this may trigger some event tasks (cloning or summation)
                                 _threadPool.waitForDone();
                          
                                 _securePendingEvents.lock();
                                 if (_pendingEventTasks.size())
                                     _waitPendingEvents.wait(&_securePendingEvents);
                                 _securePendingEvents.unlock();
                          
                                 // wait for potential new Tasks that have been triggered
                                 _threadPool.waitForDone();
                             }
                          };
                          
                          And basically, my ComputationTasks emit eventTaskDone at the end of the processing slot and they have readyForDestruction connected to deleteLater
                          
                          kshegunovK 1 Reply Last reply
                          0
                          • fcarneyF Offline
                            fcarneyF Offline
                            fcarney
                            wrote on last edited by
                            #13

                            Something to consider. Not related to threading.

                            C++ is a perfectly valid school of magic.

                            1 Reply Last reply
                            0
                            • mbruelM mbruel

                              @fcarney said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                              Is there a safe way to have a shared counter that is incremented when a thread starts and decremented when it ends? Probably through some sort of atomic operation. Once that counter reaches 0 all threads are done.

                              Hello,
                              What I need to do is to wait that the event queue of my _eventThread is consumed. There is only one Thread where several hundreds workers have been moved and are supposed to be triggered.
                              The thing is that I want to wait that first they are all started and then after that they are finished. The issue I have is to make sure they are all started. They will be started sequentially in the event loop of _eventThread. That is why I wanted to be able to interact directly on the event loop to know how many pending event it has and when it will have processed all. (knowing that no new events will be generated)

                              For now, this what I've done:
                              I'm holding a list of all the pending event Tasks. In my waitForDone method, I use a WaitCondition that is waken when an event Task finishes if the list is empty.
                              It kind of seem to work.
                              Any comments / advices?

                              class LazyComputationManager
                              {
                              private:
                                 QThreadPool             _threadPool;
                                 
                                 QThread                 _eventThread;
                                 QList<ComputationTask*> _pendingEventTasks;
                                 QWaitCondition          _waitPendingEvents;
                                 QMutex                  _securePendingEvents;
                                 
                              public:
                              
                                 ComputationTask *_appendEventTask(ComputationTask *task)
                                 {
                                     task->moveToThread(&_eventThread);
                                     connect(task, &ComputationTask::eventTaskDone, this, &LazyComputationManager::handleEventTaskDone);
                              
                                     QMutexLocker lockEventThread(&_securePendingEvents);
                                     _pendingEventTasks.append(task);
                                     return task;
                                 }
                              
                                 void handleEventTaskDone(ComputationTask *task)
                                 {
                                     QMutexLocker lockEventThread(&_securePendingEvents);
                                     _pendingEventTasks.removeOne(task);
                              
                                     if (_pendingEventTasks.isEmpty())
                                         _waitPendingEvents.wakeAll();
                              
                                     emit task->readyForDestruction(); // this trigger the destruction of the task (deleteLater)
                                 }
                              
                                 void waitForDone()
                                 {
                                     // this may trigger some event tasks (cloning or summation)
                                     _threadPool.waitForDone();
                              
                                     _securePendingEvents.lock();
                                     if (_pendingEventTasks.size())
                                         _waitPendingEvents.wait(&_securePendingEvents);
                                     _securePendingEvents.unlock();
                              
                                     // wait for potential new Tasks that have been triggered
                                     _threadPool.waitForDone();
                                 }
                              };
                              
                              And basically, my ComputationTasks emit eventTaskDone at the end of the processing slot and they have readyForDestruction connected to deleteLater
                              
                              kshegunovK Offline
                              kshegunovK Offline
                              kshegunov
                              Moderators
                              wrote on last edited by kshegunov
                              #14

                              @mbruel said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                              There is only one Thread where several hundreds workers have been moved and are supposed to be triggered.

                              If all the workers are living in the same thread there's little point in having mutexes, wait conditions and other synchronization primitives; nor multiple workers for that matter. Just schedule a custom event through the thread's event loop and have one worker process them one by one.

                              @Kent-Dorfman said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                              What complexity?

                              The implementation complexity they carry. A semaphore (depending on how it's implemented) is going to require a context switch into kernel space. For example, the simple implementation with a mutex, counter and a wait condition (which if memory serves me is how Qt implements them), means that when you manipulate the counter you're going to lock a mutex. If the mutex is a ordinary pthread mutex (i.e. a slow one) this is a kernel call each read/write of the counter. Even if you use a futex to spare some calls, on contention you're again going to require a context switch. Calling into the kernel is expensive, so it's not warranted if you can avoid it.

                              You don't HAVE to use the semaphore for blocking, and its access is guaranteed to be atomic.

                              True, I hadn't claimed otherwise.

                              Read and abide by the Qt Code of Conduct

                              1 Reply Last reply
                              0
                              • mbruelM Offline
                                mbruelM Offline
                                mbruel
                                wrote on last edited by
                                #15

                                @kshegunov said in QThread and EventLoop: how can I wait for all the events (workers) to be done?:

                                If all the workers are living in the same thread there's little point in having mutexes, wait conditions and other synchronization primitives; nor multiple workers for that matter. Just schedule a custom event through the thread's event loop and have one worker process them one by one.

                                Well by design of the application, I kind of need to have those multiple worker tasks as they will be triggered by more heavy computation tasks happening in a QThreadPool. When an heavy (threaded) computation finishes, a "light" pending event oriented one can be scheduled and it may trigger a new heavy computation.
                                The thing is, I don't know when they will be triggered nor in what order. This is not a problem. It will happen at a particular time .

                                At one point I need to be able to force all of them to happen and wait for them (first the heavy Tasks, then the event oriented ones, and finally again the heavy ones as the event oriented one can schedule 1 last).
                                So the event Tasks will already have been triggered and thus be scheduled in the event loop of my _eventThread. I don't see how I could use another worker to "wait for them" or "make sure they have been processed"

                                Your point about all the workers being living in the same QThread is true. But the call to waitForDone is done from another QThread. That is why I was thinking I needed Thread synchronisation and thus a WaitCondition.

                                I'm not familiar with Semaphore.
                                Could I use that instead? Would it be better? Basically, I need to have my "mainThread" to block on it until its value goes down to zero. So all the workers in _eventThread should be able to decrement the Semaphore while the main Thread is still waiting. Is it possible?

                                1 Reply Last reply
                                0
                                • mbruelM Offline
                                  mbruelM Offline
                                  mbruel
                                  wrote on last edited by mbruel
                                  #16

                                  At the end, to make sure all the events are processed as others may be triggered by the last one, I've just update my waitForDone method using QThread::eventDispatcher()->hasPendingEvents()

                                  void waitForDone()
                                  {
                                      // this may trigger some event tasks (cloning or summation)
                                      _threadPool.waitForDone();
                                  
                                  
                                      _securePendingEvents.lock();
                                      if (_eventThread.eventDispatcher()->hasPendingEvents() || _pendingEventTasks.size())
                                      {
                                          if (!_waitPendingEvents.wait(&_securePendingEvents, 10000)) // 10sec as we might wait for heavy tasks
                                          {
                                              _securePendingEvents.unlock();
                                              qCritical() << "[MB_TRACE][LazyComputationManager::waitForDone] Timeout..., there are still : " << _pendingEventTasks.size();
                                              while(_eventThread.eventDispatcher()->hasPendingEvents() || _pendingEventTasks.size())
                                              {
                                                  ComputationTask *pendingTask = _pendingEventTasks.first();
                                                  qCritical() << "[MB_TRACE][LazyComputationManager::waitForDone] pendingTask "
                                                              << pendingTask->getTypeName() << ": " << pendingTask;
                                                  pendingTask->runAsEvent(nullptr);
                                              }
                                          }
                                          else
                                              _securePendingEvents.unlock();
                                      }
                                      else
                                          _securePendingEvents.unlock();
                                  
                                      // wait for potential new Tasks that have been triggered
                                      _threadPool.waitForDone();
                                  }
                                  
                                  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