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
Forum Updated to NodeBB v4.3 + New Features

Best QThread practices

Scheduled Pinned Locked Moved Solved General and Desktop
13 Posts 7 Posters 437 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.
  • D DalePennington
    31 Mar 2025, 14:28

    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

    J Offline
    J Offline
    jsulm
    Lifetime Qt Champion
    wrote on 31 Mar 2025, 14:34 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 31 Mar 2025, 15:57
    2
    • C Offline
      C Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on 31 Mar 2025, 14:51 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
      • J jsulm
        31 Mar 2025, 14:34

        @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 31 Mar 2025, 15:57 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

        J 1 Reply Last reply 31 Mar 2025, 19:36
        0
        • D DalePennington
          31 Mar 2025, 15:57

          @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

          J Offline
          J Offline
          JoeCFD
          wrote on 31 Mar 2025, 19:36 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 2 Apr 2025, 12:42 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.

            P J 2 Replies Last reply 2 Apr 2025, 12:47
            0
            • D DalePennington
              2 Apr 2025, 12:42

              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.

              P Offline
              P Offline
              Pl45m4
              wrote on 2 Apr 2025, 12:47 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
                2 Apr 2025, 12:42

                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.

                J Online
                J Online
                JonB
                wrote on 2 Apr 2025, 13:40 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 2 Apr 2025, 14:14 last edited by DalePennington 4 Feb 2025, 15:38
                  #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

                  P 1 Reply Last reply 2 Apr 2025, 16:15
                  0
                  • D DalePennington
                    2 Apr 2025, 14:14

                    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

                    P Offline
                    P Offline
                    Pl45m4
                    wrote on 2 Apr 2025, 16:15 last edited by Pl45m4 4 Feb 2025, 16:20
                    #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 2 Apr 2025, 20:18 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 3 Apr 2025, 06:25 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 3 Apr 2025, 12:32 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 3 Apr 2025, 12:33

                          11/13

                          2 Apr 2025, 20:18

                          • Login

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