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. Best way to call a QThread worker object's function on an interval
Forum Updated to NodeBB v4.3 + New Features

Best way to call a QThread worker object's function on an interval

Scheduled Pinned Locked Moved General and Desktop
8 Posts 4 Posters 6.2k Views 1 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.
  • M Offline
    M Offline
    mnbv
    wrote on last edited by
    #1

    Qt 4.8.

    I'm using the QThread worker object model, along the lines of:

    QThread *thread = new QThread(...);
    
    Worker *worker = new Worker();
    worker->moveToThread(thread);
    connect(thread, SIGNAL(started()), worker, SLOT(initialize()));
    connect(thread, SIGNAL(finished()), worker, SLOT(cleanup());
    connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater());
    
    thread->start();
    

    Where Worker is basically:

    class Worker : public QObject {
       ...
    public slots:
       void initialize (); // init various things, on the worker's thread
       void cleanup (); // clean up various things, on the worker's thread
       void dostuff (); // must be called at regular intervals, on the worker's thread
    }
    

    And I want dostuff() to be called, on the worker's thread, every 20 ms (with as much accuracy as reasonably possible).

    My question is: What's the best way to set this up?

    Qt provides a lot of timer facilities, and also there's the option of which thread the timer itself runs on -- e.g. in the created thread's event loop vs. sending signals from another thread. I'd prefer the former.

    My first thought was to subclass QThread, implement timerEvent(), have it emit some signal, the connect that signal to the worker's dostuff() slot, but it's not entirely clear to me which thread timerEvent() runs on (given that the created thread itself lives in the main thread), and I'd rather not introduce an unnecessary Qt::QueuedConnection call. I'd prefer direct.

    What's the typical best practice here?

    Thanks!

    1 Reply Last reply
    0
    • M Offline
      M Offline
      mnbv
      wrote on last edited by
      #2

      I think I figured this out but I would still like some confirmation. One way is to make a QTimer, then move it to the worker thread, then connect appropriately. So something like:

      QThread *thread = new QThread(...);
      
      Worker *worker = new Worker();
      worker->moveToThread(thread);
      connect(thread, SIGNAL(started()), worker, SLOT(initialize()));
      connect(thread, SIGNAL(finished()), worker, SLOT(cleanup());
      connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater());
      
      QTimer *timer = new QTimer();
      timer->setInterval(20);
      timer->moveToThread(thread);
      connect(thread, SIGNAL(started()), timer, SLOT(start()));
      connect(thread, SIGNAL(finished()), timer, SLOT(deleteLater()));
      
      connect(timer, SIGNAL(timeout()), worker, SLOT(dostuff()));
      
      thread->start();
      

      And then, if I'm not mistaken, the timer will run in the worker thread and Worker::dostuff() will be called directly at an interval on that thread. Right? Is this the simplest approach?

      1 Reply Last reply
      1
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #3

        Hi,

        You could also use startTimer and call your function from timerEvent.

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        M 1 Reply Last reply
        1
        • SGaistS SGaist

          Hi,

          You could also use startTimer and call your function from timerEvent.

          M Offline
          M Offline
          mnbv
          wrote on last edited by mnbv
          #4

          @SGaist said in Best way to call a QThread worker object's function on an interval:

          Hi,

          You could also use startTimer and call your function from timerEvent.

          So just to make sure: timerEvent is invoked on the new thread, rather than on its parent thread, then, right?

          http://doc.qt.io/qt-4.8/qthread.html#details makes it clear that the QThread's slots are invoked on the parent thread, I am not clear on whether or not that is also true for events.

          jsulmJ 1 Reply Last reply
          0
          • M mnbv

            @SGaist said in Best way to call a QThread worker object's function on an interval:

            Hi,

            You could also use startTimer and call your function from timerEvent.

            So just to make sure: timerEvent is invoked on the new thread, rather than on its parent thread, then, right?

            http://doc.qt.io/qt-4.8/qthread.html#details makes it clear that the QThread's slots are invoked on the parent thread, I am not clear on whether or not that is also true for events.

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

            @JCipriani Why not just add the timer to Worker? Like:

            class Worker : public QObject {
               ...
            public slots:
               void initialize (); // init various things, on the worker's thread
               void cleanup (); // clean up various things, on the worker's thread
               void dostuff (); // must be called at regular intervals, on the worker's thread
            private:
                QTimer timer;
            }
            

            No need to declare it in the main thread and then move it to the worker thread.

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

            1 Reply Last reply
            3
            • thamT Offline
              thamT Offline
              tham
              wrote on last edited by tham
              #6

              I am interesting about this topic so I give it a try.

              test.h

              #ifndef TEST_H
              #define TEST_H
              
              #include <QDebug>
              #include <QThread>
              #include <QTimer>
              
              class Worker : public QObject
              {
                  Q_OBJECT
              
                  QTimer timer;
              
              public:
                  Worker()
                  {
                      //do work execute in main thread
                      //connect(&timer, QTimer::timeout, this, &Worker::doWork, Qt::DirectConnection);
                      //timer.start(1000);
              
                      //do work execute in main thread
                      //QTimer::singleShot(200, this, &Worker::doWork);
              
                      //do work execute in another thread
                      //startTimer(1000);
                  }
              
              protected:
                  void timerEvent(QTimerEvent*) override
                  {
                      qDebug()<<__func__<< " : "<<QThread::currentThread();
                  }
              
              public slots:
                  void doWork()
                  {
                    qDebug()<<__func__<< " : "<<QThread::currentThread();
                  }
              };
              
              class Controller : public QObject
              {
                  Q_OBJECT
                  QThread workerThread;
              public:
                  Controller() {
                      Worker *worker = new Worker;
                      worker->moveToThread(&workerThread);
                      connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
                      connect(this, &Controller::doWorkSignal, worker, &Worker::doWork);
              
                      QTimer *timer = new QTimer;
                      timer->setInterval(1000);
                      timer->moveToThread(&workerThread);
                      connect(&workerThread, SIGNAL(started()), timer, SLOT(start()));
                      connect(&workerThread, SIGNAL(finished()), timer, SLOT(deleteLater()));
              
                      //doWork execute in another thread
                      connect(timer, SIGNAL(timeout()), worker, SLOT(doWork()), Qt::DirectConnection);
              
                      workerThread.start();
                  }
                  ~Controller() {
                      workerThread.quit();
                      workerThread.wait();
                  }
              
              signals:
                  void doWorkSignal();
              };
              
              class test
              {
              public:
                  test();
              };
              
              #endif // TEST_H
              

              main.cpp

              #include "mainwindow.h"
              #include "test.h"
              #include <QApplication>
              
              int main(int argc, char *argv[])
              {
                  QApplication a(argc, argv);
              
                  qDebug()<< " main thread "<<QThread::currentThread();
              
                  Controller cont;    
              
                  MainWindow w;
                  w.show();
              
                  return a.exec();
              }
              

              In short, the solutions provided by @jsulm or @Sgaist and yours can execute doWork in another thread(specify the connection type by direct connection)

              However, solution of @jsulm need Qt::QueuedConnection if you need it to execute in another thread.

              But I am not sure about @Sgaist solution rely on Qt::QueuedConnection or not.

              May I ask you why you do not want any queued connection?Thanks

              M 1 Reply Last reply
              2
              • thamT tham

                I am interesting about this topic so I give it a try.

                test.h

                #ifndef TEST_H
                #define TEST_H
                
                #include <QDebug>
                #include <QThread>
                #include <QTimer>
                
                class Worker : public QObject
                {
                    Q_OBJECT
                
                    QTimer timer;
                
                public:
                    Worker()
                    {
                        //do work execute in main thread
                        //connect(&timer, QTimer::timeout, this, &Worker::doWork, Qt::DirectConnection);
                        //timer.start(1000);
                
                        //do work execute in main thread
                        //QTimer::singleShot(200, this, &Worker::doWork);
                
                        //do work execute in another thread
                        //startTimer(1000);
                    }
                
                protected:
                    void timerEvent(QTimerEvent*) override
                    {
                        qDebug()<<__func__<< " : "<<QThread::currentThread();
                    }
                
                public slots:
                    void doWork()
                    {
                      qDebug()<<__func__<< " : "<<QThread::currentThread();
                    }
                };
                
                class Controller : public QObject
                {
                    Q_OBJECT
                    QThread workerThread;
                public:
                    Controller() {
                        Worker *worker = new Worker;
                        worker->moveToThread(&workerThread);
                        connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
                        connect(this, &Controller::doWorkSignal, worker, &Worker::doWork);
                
                        QTimer *timer = new QTimer;
                        timer->setInterval(1000);
                        timer->moveToThread(&workerThread);
                        connect(&workerThread, SIGNAL(started()), timer, SLOT(start()));
                        connect(&workerThread, SIGNAL(finished()), timer, SLOT(deleteLater()));
                
                        //doWork execute in another thread
                        connect(timer, SIGNAL(timeout()), worker, SLOT(doWork()), Qt::DirectConnection);
                
                        workerThread.start();
                    }
                    ~Controller() {
                        workerThread.quit();
                        workerThread.wait();
                    }
                
                signals:
                    void doWorkSignal();
                };
                
                class test
                {
                public:
                    test();
                };
                
                #endif // TEST_H
                

                main.cpp

                #include "mainwindow.h"
                #include "test.h"
                #include <QApplication>
                
                int main(int argc, char *argv[])
                {
                    QApplication a(argc, argv);
                
                    qDebug()<< " main thread "<<QThread::currentThread();
                
                    Controller cont;    
                
                    MainWindow w;
                    w.show();
                
                    return a.exec();
                }
                

                In short, the solutions provided by @jsulm or @Sgaist and yours can execute doWork in another thread(specify the connection type by direct connection)

                However, solution of @jsulm need Qt::QueuedConnection if you need it to execute in another thread.

                But I am not sure about @Sgaist solution rely on Qt::QueuedConnection or not.

                May I ask you why you do not want any queued connection?Thanks

                M Offline
                M Offline
                mnbv
                wrote on last edited by mnbv
                #7

                @tham said in Best way to call a QThread worker object's function on an interval:

                However, solution of @jsulm need Qt::QueuedConnection if you need it to execute in another thread.

                Nice test, thank you.

                Well, @jsulm's solution can be made to work with Qt::DirectConnection by setting the Worker as the QTimer's parent, e.g. in the Worker constructor:

                    Worker() : timer(this)
                    {
                        ...
                    }
                

                That way the timer is also moved to the new thread along with the Worker. Direct connection or not, this should probably be done anyways, since it also solves the problem of attempting to kill the timer from a different thread (Qt complains, at least 4.8 does) because the Worker's destructor is called on the new thread (presuming you connect QThread::finished to Worker::deleteLater). It also solves a spurious related crash on cleanup (mingw, Windows 7).

                May I ask you why you do not want any queued connection?Thanks

                It's complicated and also after some testing I'm not sure that I do any more (doWork() might take longer than a timer interval and I didn't want signals to queue up, but thinking about it, that doesn't really matter; also I was concerned about inconsistent latency, but testing seems to show there's no effect and also thinking about it that doesn't matter either; in conclusion I think I was being silly). So... I'll get back to you on that one... 8)

                In any case it's still an informative exercise.


                For completeness:

                class Worker : public QObject {
                    ...
                public:
                    Worker (...) : timer(this) { ... }
                public slots:
                    void initialize (); // init various things, on the worker's thread
                    void cleanup (); // clean up various things, on the worker's thread
                    void dostuff (); // must be called at regular intervals, on the worker's thread
                private:
                    QTimer timer;
                }
                
                jsulmJ 1 Reply Last reply
                1
                • M mnbv

                  @tham said in Best way to call a QThread worker object's function on an interval:

                  However, solution of @jsulm need Qt::QueuedConnection if you need it to execute in another thread.

                  Nice test, thank you.

                  Well, @jsulm's solution can be made to work with Qt::DirectConnection by setting the Worker as the QTimer's parent, e.g. in the Worker constructor:

                      Worker() : timer(this)
                      {
                          ...
                      }
                  

                  That way the timer is also moved to the new thread along with the Worker. Direct connection or not, this should probably be done anyways, since it also solves the problem of attempting to kill the timer from a different thread (Qt complains, at least 4.8 does) because the Worker's destructor is called on the new thread (presuming you connect QThread::finished to Worker::deleteLater). It also solves a spurious related crash on cleanup (mingw, Windows 7).

                  May I ask you why you do not want any queued connection?Thanks

                  It's complicated and also after some testing I'm not sure that I do any more (doWork() might take longer than a timer interval and I didn't want signals to queue up, but thinking about it, that doesn't really matter; also I was concerned about inconsistent latency, but testing seems to show there's no effect and also thinking about it that doesn't matter either; in conclusion I think I was being silly). So... I'll get back to you on that one... 8)

                  In any case it's still an informative exercise.


                  For completeness:

                  class Worker : public QObject {
                      ...
                  public:
                      Worker (...) : timer(this) { ... }
                  public slots:
                      void initialize (); // init various things, on the worker's thread
                      void cleanup (); // clean up various things, on the worker's thread
                      void dostuff (); // must be called at regular intervals, on the worker's thread
                  private:
                      QTimer timer;
                  }
                  
                  jsulmJ Offline
                  jsulmJ Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  @JCipriani Yes, there is no need for queued connection as both Worker and the timer are in the same thread.

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

                  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