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 QThread practices
QtWS25 Last Chance

Best QThread practices

Scheduled Pinned Locked Moved Solved General and Desktop
13 Posts 7 Posters 378 Views
  • 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.
  • D Offline
    D Offline
    DalePennington
    wrote on last edited by
    #1

    Folks,

    I have a thread I want to implement as a QThread. It is a fairly common process thread that would be (in pthreads)

    void ThreadFunction()
    {
        // Open Log File
       
        while (!done)
        {
            // Process and write to log file
            usleep(100000);
        }
    
        /// Close Log File
    }
    

    A) Set up start in thread (such as opening a log file)
    B) Run loop doing process (process,wait cycle)
    C) Clean up thread (such as closing a log file)

    What I was wondering was what was considered best practice with QThreads. I had 2 ideas.

    The first is to do it in the run function. The logic here would look like

    MyThread::run()
    {
        // Open log file
        // Set up process loop timer
        connect(timer, SIGNAL(timeout()), this, SLOT(Process()));
        timer->start(100);
    
        // Run loop
        exec();
    
        // Close log file
    }
    
    MyThread::Process()
    {
        // Process and write to log file
    }
    

    The second would be to use the started and finished slots

    MyThread::MyThread(QObject *parent):QThread(parent)
    {
        connect(this,SIGNAL(started(),this,SLOT(StartThread()));
        connect(this,SIGNAL(finished(),this,SLOT(FinishThread()));
    }
    
    MyThread::StartThread()
    {
        // Open log file
        // Set up process loop timer
        connect(timer, SIGNAL(timeout()), this, SLOT(Process()));
        timer->start(100);
    }
    
    MyThread::FinishThread()
    {
        // Close log file
    }
    
    MyThread::Process()
    {
        // Process and write to log file
    }
    

    Obviously one question would be if StartThread in the second option was in the context of the running thread. If not, then second option is a non-starter.

    The point to either of these is too allow a process loop, so QThread::quit() can be used as needed. As noted above, the real question above, is which is considered the best practice?

    Thanks,
    Dale Pennington

    jsulmJ 1 Reply Last reply
    0
    • D DalePennington

      Folks,

      I have a thread I want to implement as a QThread. It is a fairly common process thread that would be (in pthreads)

      void ThreadFunction()
      {
          // Open Log File
         
          while (!done)
          {
              // Process and write to log file
              usleep(100000);
          }
      
          /// Close Log File
      }
      

      A) Set up start in thread (such as opening a log file)
      B) Run loop doing process (process,wait cycle)
      C) Clean up thread (such as closing a log file)

      What I was wondering was what was considered best practice with QThreads. I had 2 ideas.

      The first is to do it in the run function. The logic here would look like

      MyThread::run()
      {
          // Open log file
          // Set up process loop timer
          connect(timer, SIGNAL(timeout()), this, SLOT(Process()));
          timer->start(100);
      
          // Run loop
          exec();
      
          // Close log file
      }
      
      MyThread::Process()
      {
          // Process and write to log file
      }
      

      The second would be to use the started and finished slots

      MyThread::MyThread(QObject *parent):QThread(parent)
      {
          connect(this,SIGNAL(started(),this,SLOT(StartThread()));
          connect(this,SIGNAL(finished(),this,SLOT(FinishThread()));
      }
      
      MyThread::StartThread()
      {
          // Open log file
          // Set up process loop timer
          connect(timer, SIGNAL(timeout()), this, SLOT(Process()));
          timer->start(100);
      }
      
      MyThread::FinishThread()
      {
          // Close log file
      }
      
      MyThread::Process()
      {
          // Process and write to log file
      }
      

      Obviously one question would be if StartThread in the second option was in the context of the running thread. If not, then second option is a non-starter.

      The point to either of these is too allow a process loop, so QThread::quit() can be used as needed. As noted above, the real question above, is which is considered the best practice?

      Thanks,
      Dale Pennington

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

      @DalePennington I would suggest a third approach: worker object. This way there is no need to subclass QThread.
      See example here: https://doc.qt.io/qt-6/qthread.html

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

      D 1 Reply Last reply
      2
      • Christian EhrlicherC Offline
        Christian EhrlicherC Offline
        Christian Ehrlicher
        Lifetime Qt Champion
        wrote on last edited by
        #3

        The second one will not work as 'timer' lives in the main thread

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

        1 Reply Last reply
        3
        • jsulmJ jsulm

          @DalePennington I would suggest a third approach: worker object. This way there is no need to subclass QThread.
          See example here: https://doc.qt.io/qt-6/qthread.html

          D Offline
          D Offline
          DalePennington
          wrote on last edited by
          #4

          @jsulm I was not looking at worker object first, because it seemed to add complications. Also it looks like doWork is called once, and keeps running to completion, so no time outs for any for event processing. Note that this thread would basically run from app start to app shutdown. So how could such an object for example receive an incoming signal ?

          Dale

          JoeCFDJ 1 Reply Last reply
          0
          • D DalePennington

            @jsulm I was not looking at worker object first, because it seemed to add complications. Also it looks like doWork is called once, and keeps running to completion, so no time outs for any for event processing. Note that this thread would basically run from app start to app shutdown. So how could such an object for example receive an incoming signal ?

            Dale

            JoeCFDJ Offline
            JoeCFDJ Offline
            JoeCFD
            wrote on last edited by
            #5

            @DalePennington worker is an object. Connect worker with any widget or object which sends signals to worker.

            1 Reply Last reply
            0
            • D Offline
              D Offline
              DalePennington
              wrote on last edited by
              #6

              I am clearly missing something about how the worker object really works. The example in the QThread documentation is incomplete. But it looks to me like the doWork, when kicked off, runs during completion, not performing a slice of work. I assume while doWork is executing, there is not event processing occuring on that thread, so no events delivered.

              Pl45m4P JonBJ 2 Replies Last reply
              0
              • D DalePennington

                I am clearly missing something about how the worker object really works. The example in the QThread documentation is incomplete. But it looks to me like the doWork, when kicked off, runs during completion, not performing a slice of work. I assume while doWork is executing, there is not event processing occuring on that thread, so no events delivered.

                Pl45m4P Online
                Pl45m4P Online
                Pl45m4
                wrote on last edited by
                #7

                @DalePennington

                Compare this table to your needs:

                • https://doc.qt.io/qt-6/threads-technologies.html#example-use-cases

                A worker QObject can be controlled using signals that you emit from your main thread or object. In the same way you can get data back or even stop/restart your worker.


                If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                ~E. W. Dijkstra

                1 Reply Last reply
                0
                • D DalePennington

                  I am clearly missing something about how the worker object really works. The example in the QThread documentation is incomplete. But it looks to me like the doWork, when kicked off, runs during completion, not performing a slice of work. I assume while doWork is executing, there is not event processing occuring on that thread, so no events delivered.

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by
                  #8

                  @DalePennington said in Best QThread practices:

                  But it looks to me like the doWork, when kicked off, runs during completion, not performing a slice of work. I assume while doWork is executing, there is not event processing occuring on that thread, so no events delivered.

                  Correct.

                  Unless you do some explicit processEvents() yourself in the thread, which is no more advisable than doing so in the main thread event loop. Just saying for completeness.

                  1 Reply Last reply
                  0
                  • D Offline
                    D Offline
                    DalePennington
                    wrote on last edited by DalePennington
                    #9

                    OK, I see the table saying use a worker object, but then JonB says that when doWork is running, I cannot get signals into the thread for processing. I assume signals can go out, but how do I get them in (for example, having a controlled shutdown on work rather than just quiting under it). Do I just have to treat it like a stock unix thread and have QMutexes and QWaitConditions around booleans to communcate into the processing thread ?

                    As a follow-on I now have worker pattern working. I did this by having doWork just initialize what I need and then use QTimers and Slots to cause breaks for events to show up. I am still having an issue on how to clean up when the main window is closed. I emit a signal to the worker but it does not get processed before the app shuts down. Since I want the cleanup to process within the context of the worker thread, how do I get it to cleanup on program exit ?

                    Thanks for the time
                    Dale

                    Pl45m4P 1 Reply Last reply
                    0
                    • D DalePennington

                      OK, I see the table saying use a worker object, but then JonB says that when doWork is running, I cannot get signals into the thread for processing. I assume signals can go out, but how do I get them in (for example, having a controlled shutdown on work rather than just quiting under it). Do I just have to treat it like a stock unix thread and have QMutexes and QWaitConditions around booleans to communcate into the processing thread ?

                      As a follow-on I now have worker pattern working. I did this by having doWork just initialize what I need and then use QTimers and Slots to cause breaks for events to show up. I am still having an issue on how to clean up when the main window is closed. I emit a signal to the worker but it does not get processed before the app shuts down. Since I want the cleanup to process within the context of the worker thread, how do I get it to cleanup on program exit ?

                      Thanks for the time
                      Dale

                      Pl45m4P Online
                      Pl45m4P Online
                      Pl45m4
                      wrote on last edited by Pl45m4
                      #10

                      @DalePennington said in Best QThread practices:

                      As a follow-on I now have worker pattern working. I did this by having doWork just initialize what I need and then use QTimers and Slots to cause breaks for events to show up.

                      No!
                      You don't need timers to "interrupt" your process.

                      • https://wiki.qt.io/QThreads_general_usage

                      As far as I understand your initial idea of parsing/writing log files, you can create your object, let's say "LogFileWorker", and start it while passing a filename for example with a start(filename) signal connected to the worker slot which processes the file.
                      Once the worker is finished (or even in between) you can send results/signals to the main thread.
                      This whole procedure can be repeated whenever you want to and wherever you call worker->start (or whatever you call your signal/function to init the start of your worker/thread).

                      The main difference to your QThread::run() approach is that you do receive signals via your QObject worker that lives in a plain and simple QThread ( Qt's thread wrapper class), which in turn does not require any further configurations.

                      Edit:
                      In regards to proper cleanup, also read the article I've linked above in this post here
                      (it's also done through utilization of cross-thread signals calling QObject::deleteLater for example)


                      If debugging is the process of removing software bugs, then programming must be the process of putting them in.

                      ~E. W. Dijkstra

                      1 Reply Last reply
                      0
                      • D Offline
                        D Offline
                        DalePennington
                        wrote on last edited by
                        #11

                        Actually what each thread is doing is getting input from a socket via a third party library (each thread is one "channel" which it its own socket). For now we are logging the data in a file, but later will be parsing some data and passing it along elsewhere in the app. So the thread runs constantly from start till the app is shut down.

                        Without the timer breakup, the process thread would never get any slots invoked as it would never give up processing in the thread. Also the cleanup issue I am concerned with is when the operator closes the app (for example via hitting the X button in the upper right corner). At that point I want each thread to close its log file so no data is lost. Since the app is being exited the deleteLater never occurs so the destructor is not called, so that is not a valid cleanup method (I put some debug prints to make sure this is the case).

                        1 Reply Last reply
                        0
                        • S Offline
                          S Offline
                          SimonSchroeder
                          wrote on last edited by
                          #12

                          I would say that initially you weren't fully on the wrong track. You just need to move everything out of QThread and into a worker object. If you are lazy it could even be just separate lambdas:

                          QThread *thread = new QThread();
                          connect(thread, &QThread::startet, []() { /* initialize */ });
                          connect(thread, &QThread::finished, []() { /* cleanup */ });
                          connect(thread, &QThread::finished, thread, &QThread::deleteLater);
                          QMetaObject::invokeMethod(thread, []() { /* do work */ });
                          thread->start();
                          

                          Notice the use of QMetaObject::invokeMethod to explicitly put a function call into the event loop of the thread. Using a worker object with slots instead of the lambdas is the cleaner approach.

                          What you should never do is have an infinite loop for processing. Usually, there should be a trigger (i.e. a signal) to do another round of processing. However, sometimes you need to poll information in which case the timer could be a right choice. There is a trick with using a QTimer with timeout of 0 ms. This means that whenever the thread is idle the timer will timout. Note that "whenever the thread is idle" also means that one CPU core will be fully utilized constantly. Having a timeout of a few milliseconds can reduce that.

                          Another way with a worker object (which would also use up one CPU core) would be the following: Suppose you have a signal WorkerObject::triggerProcessing() which is connected to a slot WorkerObject::process(). Instead of

                          void WorkerObject::process()
                          {
                              while(!done)
                              {
                                  // do work
                              }
                          }
                          

                          you can do this:

                          void WorkerObject::process()
                          {
                              if(!done)
                              {
                                  // do work
                                  if(!done) //still not done
                                      emit triggerProcessing(); // do another round of processing
                              }
                          }
                          

                          In this case you would launch the first round of processing using QMetaObject::invokeMethod on WorkerObject::process.

                          Without knowing what you want to achieve it is hard to tell which approach would be most appropriate in your case. The most important thing, though, is: Don't use an infinite loop (with a break condition) inside a running event loop.

                          1 Reply Last reply
                          0
                          • D Offline
                            D Offline
                            DalePennington
                            wrote on last edited by
                            #13

                            That is basically the approach I am using. Only with a short timer to give up the core for a little bit. Messages average about 1/sec, but we want quick response as part of this effort is to do some timing analysis before creating the final app.

                            I had looked at finish early on as an early approach, but at that point I was looking for an "in-thread" use with a subclassed QThread, and the slot invocations in that case were in the parent thread, not wanted. However, using the Worker pattern, when the finished signal function is called, it is even in the worker thread, which appears to solve my problem for now.

                            Thanks,
                            Dale

                            1 Reply Last reply
                            0
                            • D DalePennington has marked this topic as solved on

                            • Login

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