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. [SOLVED] Proper handling of QThread on main window close

[SOLVED] Proper handling of QThread on main window close

Scheduled Pinned Locked Moved General and Desktop
qthreadmainwindowexit
11 Posts 4 Posters 18.9k 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.
  • V Offline
    V Offline
    voidtrance
    wrote on last edited by voidtrance
    #1

    Hi all,

    I've just started learning Qt and while most of it seems relatively easy to understand, I am having an issue with properly handling QThreads. Particularly, I can't figure out what's the proper way to terminate a running QThread when the overall application exits.

    For example, I have a worker object defined as

    void FileSearchWorker::search()
    {
        quint32 found;
        found = recursiveSearch(dir);
        emit finished(found);
    }
    
    quint32 FileSearchWorker::recursiveSearch(const QDir &dir)
    {
        quint32 count = 0;
        QFileInfoList entries;
        if (dir.exists()) {
            entries = dir.entryInfoList(QDir::Files | QDir::NoSymLinks);
            for (int i = 0; i < entries.size(); i++) {
                bool is_match = false;
                if (pattern) {
                    if (pattern->exactMatch(entries.at(i).canonicalFilePath()))
                        is_match = true;
                } else
                    is_match = true;
    
                if (is_match) {
                    count++;
                    emit fileFound(entries.at(i).canonicalFilePath());
                }
            }
    
            entries = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks);
            for (int i = 0; i < entries.size(); i++)
                count += recursiveSearch(entries.at(i).canonicalFilePath());
        }
        return count;
    }
    

    In another class in the main thread, I have the following code which creates and sets up the worker object:

    quint32 Cscope::findAllFiles(const QDir& dir)
    {
        thread = new QThread;
        FileSearchWorker *worker = new FileSearchWorker(dir);
    
        worker->moveToThread(thread);
        connect(thread, SIGNAL(started()), worker, SLOT(search()));
        connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
        connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
        connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    
        connect(worker, SIGNAL(finished(quint32)), this, SLOT(searchFileDone(quint32)));
    
        thread->start();
        thread_is_running = true;
        while (thread_is_running)
            qApp->processEvents();
        return filesFound;
    }
    

    I've also connected the application's aboutToQuit() signal and the main window's destroyed() signal to a slot which calls thread->exit().

    The problem is that when the main window is closed, the slot to terminate the thread does not get called until the thread is finished. Considering that the main thread is spinning on thread_is_running and executing the main even loop, this does not make sense to me. I can't figure out why the terminate slot is getting called after the thread terminates on its own.

    Can someone please explain to me how to properly terminate a running thread when the main window of the application is closed?

    Thank you in advance for your advice!

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

      Why are you doing this?

        while (thread_is_running)
           qApp->processEvents();
      

      Anyway, keep in mind that calling the thread::exit method will call QEventLoop::exit, and the EventLoop will only terminate once the control is returned to the EventLoop. In your case this will happen after the FileSearchWorker::search has finished.

      So you should do the following:

                           your slot connected to the aboutToQuit signal
                            {
                             thread->quit();
                             thread->wait();
                           }
      

      If you want to terminate a thread right away you have to use QThread::terminate but you shouldn't do that.

      1 Reply Last reply
      0
      • V Offline
        V Offline
        voidtrance
        wrote on last edited by
        #3

        Thank you for your reply.

        I had the while loop because I wanted a blocking semantic from that function (it was just to test QThread). However, I didn't want to block the entire application, so I decided on spinning and processing events.

        The issue that I was having is not that I was using Qthread::exit() but rather that the slot which called thread->exit() did not get executed until the thread was done. The end effect was that the main thread event loop appeared serialized with the QThread event loop.

        I would have expected that the two event loops are disjoint and that when a signal is emitted in the main event loop the slot is called as soon as the main even loop processes that event regardless of the state of the QThread's event loop.

        1 Reply Last reply
        0
        • R Offline
          R Offline
          Rondog
          wrote on last edited by
          #4

          I don't know if this the best way but for programs that have threads I have a custom ::closeEvent(QCloseEvent *event) function. When the application is closed if any threads are still running the close event is ignored and the threads are shutdow (cleanly) at that point. The closeEvent() is refired using a QTimer after a short delay and keeps doing this until all threads are stopped.

          V 1 Reply Last reply
          1
          • R Rondog

            I don't know if this the best way but for programs that have threads I have a custom ::closeEvent(QCloseEvent *event) function. When the application is closed if any threads are still running the close event is ignored and the threads are shutdow (cleanly) at that point. The closeEvent() is refired using a QTimer after a short delay and keeps doing this until all threads are stopped.

            V Offline
            V Offline
            voidtrance
            wrote on last edited by voidtrance
            #5

            @Rondog Thank you for the response and for the idea. I may have to do something similar. However, I cannot imagine that this is the way QThreads were intended to be used. There has to be a proper way to shut down running threads, especially considering that event-based GUI toolkits are asynchronous by nature.

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

              Hi and welcome to devnet,

              Where is thread_is_running set to false ?

              Anyway, if you need a blocking behavior AND since you are using a thread, you can use the BlockingQueuedConnection rather than a while loop. One other thing to do is to design your worker to be cancelable so you can break it when needed. One other thing to check is QtConcurrent, it's higher level and might also provide you with a simpler solution.

              Finally, do you really need it to be blocking ?

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

              V 1 Reply Last reply
              0
              • SGaistS SGaist

                Hi and welcome to devnet,

                Where is thread_is_running set to false ?

                Anyway, if you need a blocking behavior AND since you are using a thread, you can use the BlockingQueuedConnection rather than a while loop. One other thing to do is to design your worker to be cancelable so you can break it when needed. One other thing to check is QtConcurrent, it's higher level and might also provide you with a simpler solution.

                Finally, do you really need it to be blocking ?

                V Offline
                V Offline
                voidtrance
                wrote on last edited by
                #7

                @SGaist Hi, thank you for your reply. The blocking semantic was for testing purposes only. It wouldn't make much sense to keep it blocking but also use QThread.

                All of the suggestions on this thread are great but they still don't address the main problem - the fact that the event ("aboutToQuit") which is supposed to signal the cancellation of the thread was not being processed until after the thread exited. Below, I've include some output from my application illustrating the issue:

                This first output is from an uninterrupted, complete run:

                1431358733329 search search process done
                1431358733329 sourceThreadDone found 37755 files List size: 37755
                1431358738989 on_pushButton_2_clicked exit button clicked
                1431358738989 close close firing
                1431358738989 terminate terminate called false

                As you can see, the process found 37755 files in 4889 msecs. The second output is from a run in which I hit the exit button (which is supposed to exit the entire application):

                1431358767141 scan 49289
                1431358769218 on_pushButton_2_clicked exit button clicked
                1431358769219 close close firing
                1431358769219 terminate terminate called true
                1431358769219 terminate stopping thread
                1431358772037 search search process done
                1431358772055 sourceThreadDone found 37755 files List size: 37755

                When the thread exited, it had still found the same number of files and ran for 4914 msec. This is where the "cancelable worker" would come in handy. Finally, a run where I hit the 'X' button on the window decoration:

                1431359204324 scan 49289
                1431359209146 search search process done
                1431359209146 sourceThreadDone found 37755 files List size: 37755
                1431359209177 close close firing
                1431359209178 terminate terminate called false

                In this last run, no events have fired when the application was closed. Mind you, I have mainWindows::destroyed() connected to the same slot as QApplication::aboutToQuit() so theoretically, the same slot should be called when the "X" buttons is pressed.

                1 Reply Last reply
                0
                • V Offline
                  V Offline
                  voidtrance
                  wrote on last edited by
                  #8

                  I played around with the code a bit more and I have found that subclassing QThread offers better thread management in my use case.

                  Now, I still have to figure out how to properly terminate the thread if the "X" button is clicked on the window's title bar.

                  1 Reply Last reply
                  0
                  • R Offline
                    R Offline
                    Rondog
                    wrote on last edited by Rondog
                    #9

                    I found that having your own version of closeEvent() will be called regardless of how the application is closed (including the 'X' at the top).

                    I think I have looked at QApplication::aboutToQuit() before and found it didn't fire under all conditions. I tend to stick to what works for me so I don't regularly look at alternatives.

                    I prefer sub-classing QThread as I can add options to shut it down in a way that is clean and fast. I usually add a member called 'Stop_Thread()' which sets a boolean value to true and is tested at an appropriate place in the thread itself:

                    void MyThread::run(void)
                    {
                      d_thread_cancelled = false;  // indicates data is usable
                    
                      do
                      {
                        if(d_stop_thread)
                        {
                          d_thread_cancelled = true;
                          return;
                        }
                    
                      // something thread related
                    
                      } while(!done);  // thread runs until it is done
                    }
                    V 1 Reply Last reply
                    0
                    • R Rondog

                      I found that having your own version of closeEvent() will be called regardless of how the application is closed (including the 'X' at the top).

                      I think I have looked at QApplication::aboutToQuit() before and found it didn't fire under all conditions. I tend to stick to what works for me so I don't regularly look at alternatives.

                      I prefer sub-classing QThread as I can add options to shut it down in a way that is clean and fast. I usually add a member called 'Stop_Thread()' which sets a boolean value to true and is tested at an appropriate place in the thread itself:

                      void MyThread::run(void)
                      {
                        d_thread_cancelled = false;  // indicates data is usable
                      
                        do
                        {
                          if(d_stop_thread)
                          {
                            d_thread_cancelled = true;
                            return;
                          }
                      
                        // something thread related
                      
                        } while(!done);  // thread runs until it is done
                      }
                      V Offline
                      V Offline
                      voidtrance
                      wrote on last edited by
                      #10

                      @Rondog Thanks for the advice. I'll look into the closeEvent() method.

                      V 1 Reply Last reply
                      0
                      • V voidtrance

                        @Rondog Thanks for the advice. I'll look into the closeEvent() method.

                        V Offline
                        V Offline
                        voidtrance
                        wrote on last edited by
                        #11

                        @voidtrance said:

                        @Rondog Thanks for the advice. I'll look into the closeEvent() method.

                        The MainWindow::closeEvent() provided exactly what I needed.

                        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