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. Stopping and deleting background QThread and its worker

Stopping and deleting background QThread and its worker

Scheduled Pinned Locked Moved General and Desktop
16 Posts 4 Posters 17.2k 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.
  • A Offline
    A Offline
    Adam Badura
    wrote on last edited by
    #6

    Qt5

    Yet it does not happen. I both added qDebug (in my UDPListener run on the thread) and set a breakpoint in destructors (both UDPListener and QThread).

    1 Reply Last reply
    0
    • M Offline
      M Offline
      mcosta
      wrote on last edited by
      #7

      Hi Adam,

      if you want stop your UDPListener, you have to write it in such manner.
      Please post your code.

      Once your problem is solved don't forget to:

      • Mark the thread as SOLVED using the Topic Tool menu
      • Vote up the answer(s) that helped you to solve the issue

      You can embed images using (http://imgur.com/) or (http://postimage.org/)

      1 Reply Last reply
      0
      • A Offline
        A Offline
        Adam Badura
        wrote on last edited by
        #8

        Bellow is the simplified version of the code which still contains the important aspects and shows the issues.

        @#include <QtCore/QByteArray>
        #include <QtCore/QDebug>
        #include <QtCore/QObject>
        #include <QtCore/QThread>
        #include <QtCore/QtGlobal>
        #include <QtNetwork/QHostAddress>
        #include <QtNetwork/QNetworkInterface>
        #include <QtNetwork/QUdpSocket>
        #include <QtWidgets/QApplication>
        #include <QtWidgets/QMainWindow>

        // Required to use QHostAddress as parameter in signals.
        Q_DECLARE_METATYPE( QHostAddress )

        ////////////////////////////////////////////////////////////////////////////////

        class UDPListener
        : public QObject
        {
        Q_OBJECT

        public:
        UDPListener( QObject* parent = nullptr )
        : QObject( parent ),
        theSocketPtr( new QUdpSocket( this ) )
        {
        // Required to use QHostAddress as parameter in signals.
        qRegisterMetaType< QHostAddress >();

        if (
          !theSocketPtr->bind(
            QHostAddress::AnyIPv4,
            21212,
            QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint
          )
        )
        {
          Q_ASSERT( false );
        }
        
        Q_ASSERT( theSocketPtr->state() == QAbstractSocket::BoundState );
        
        if ( !theSocketPtr->joinMulticastGroup( QHostAddress( "224.0.0.66" ) ) )
        {
          Q_ASSERT( false );
        }
        

        }

        virtual ~UDPListener()
        {
        qDebug() << "UDPListener::~UDPListener()";
        }

        public slots:
        void run()
        {
        connect(
        theSocketPtr,
        &QUdpSocket::readyRead,
        this,
        &UDPListener::processDatagrams
        );

        processDatagrams();
        

        }

        private slots:
        void processDatagram()
        {
        QByteArray datagram;
        QHostAddress senderAddres;
        quint16 senderPort;

        datagram.resize( theSocketPtr->pendingDatagramSize() );
        theSocketPtr->readDatagram(
          datagram.data(),
          datagram.size(),
          &senderAddres,
          &senderPort
        );
        
        emit datagramReceived( datagram, senderAddres, senderPort );
        

        }

        void processDatagrams()
        {
        while ( theSocketPtr->hasPendingDatagrams() )
        processDatagram();
        }

        signals:
        void datagramReceived(
        const QByteArray& aDatagram,
        const QHostAddress& aSenderAddress,
        quint16 aSenderPort
        );

        private:
        QUdpSocket* theSocketPtr;

        };

        ////////////////////////////////////////////////////////////////////////////////

        class NetworkWatcher
        : public QObject
        {
        Q_OBJECT

        public:
        NetworkWatcher( QObject* parent = nullptr )
        : QObject( parent ),
        theUDPListenerThreadPtr(),
        theUDPListenerPtr()
        {
        }

        virtual ~NetworkWatcher()
        {
        qDebug() << "NetworkWatcher::~NetworkWatcher()";

        theUDPListenerThreadPtr->quit();
        

        }

        void runOnNewThread()
        {
        theUDPListenerPtr = new UDPListener();
        connect(
        theUDPListenerPtr,
        &UDPListener::datagramReceived,
        this,
        &NetworkWatcher::onDatagramReceived
        );

        theUDPListenerThreadPtr = new QThread();
        theUDPListenerThreadPtr->setObjectName( "NetworkWatcher::UDPListener" );
        
        connect(
          theUDPListenerThreadPtr,
          &QThread::started,
          theUDPListenerPtr,
          &UDPListener::run
        );
        connect(
          theUDPListenerThreadPtr,
          &QThread::finished,
          theUDPListenerPtr,
          &UDPListener::deleteLater
        );
        connect(
          theUDPListenerThreadPtr,
          &QThread::finished,
          theUDPListenerThreadPtr,
          &QThread::deleteLater
        );
        
        theUDPListenerPtr->moveToThread( theUDPListenerThreadPtr );
        theUDPListenerThreadPtr->start();
        

        }

        protected slots:
        void onDatagramReceived(
        const QByteArray& aDatagram,
        const QHostAddress& aSenderAddress,
        quint16 aSenderPort
        )
        {
        /* ... */
        }

        private:
        QThread* theUDPListenerThreadPtr;

        UDPListener* theUDPListenerPtr;

        };

        ////////////////////////////////////////////////////////////////////////////////

        class Application
        : public QApplication
        {
        Q_OBJECT

        public:
        Application( int& argc, char** argv )
        : QApplication( argc, argv ),
        theNetworkWatcherPtr( new NetworkWatcher( this ) )
        {
        theNetworkWatcherPtr->runOnNewThread();
        }

        virtual ~Application()
        {
        qDebug() << "Application::~Application()";
        }

        private:
        NetworkWatcher* theNetworkWatcherPtr;

        };

        ////////////////////////////////////////////////////////////////////////////////

        int main( int argc, char* argv[] )
        {
        Application application( argc, argv );

        QMainWindow mainWindow;
        mainWindow.show();

        return application.exec();
        }@

        Running the code shows that QThread (NetworkWatchar::theUDPListenerThreadPtr) and UDPListener (NetworkWatcher::theUDPListenerPtr) are never destroyed (nor their memory is deallocated).

        1 Reply Last reply
        0
        • M Offline
          M Offline
          mcosta
          wrote on last edited by
          #9

          Simplified code?
          I think it's a little bit complicated.

          What means object never destroyed? Destuctor are never called?

          I see that in your code you never call QThread::quit(); you should do.
          I suggest to launch the Thread in MainWindow constructor or similar and call QThread::quit() on destructor or in "closeEvent"

          Once your problem is solved don't forget to:

          • Mark the thread as SOLVED using the Topic Tool menu
          • Vote up the answer(s) that helped you to solve the issue

          You can embed images using (http://imgur.com/) or (http://postimage.org/)

          1 Reply Last reply
          0
          • K Offline
            K Offline
            KA51O
            wrote on last edited by
            #10

            I would add one more connect statement and add a slot on your UDPListener to stop him working.
            @
            void runOnNewThread()
            {
            .
            .
            .
            connect(
            theUDPListenerPtr,
            &UDPListener::finished,
            theUDPListenerThreadPtr ,
            &QThread::quit
            );
            .
            .
            .
            @

            In this slot stop() (or whatever) you can set a boolean flag to true, and then change the function UDPListener::processDatagrams to look like this:
            @
            void UDPListener::stop()
            {
            m_bStopWorking = true;
            }

            void UDPListener::processDatagrams()
            {
            while(theSocketPtr->hasPendingDatagrams())
            {
            if(!m_bStopWorking)
            {
            processDatagram();
            }
            else
            {
            emit finished();
            }
            }
            }
            @

            The while loop might be a problem though, because as long as you're in that loop control will not return to the event queue and therefor the stop() slot will not be called. I'm not sure how long your object usually spends in that loop. If it spends a lot of time in that loop you might have to think of a way to replace that loop by a signal slot connection. Maybe by connecting your datagramReceived(...) signal to your processDatagrams() slot?

            1 Reply Last reply
            0
            • A Offline
              A Offline
              Adam Badura
              wrote on last edited by
              #11

              [quote author="mcosta" date="1364427839"]What means object never destroyed? Destuctor are never called?[/quote]

              Exactly. Destructors for UDPListener and QThread are never called.

              [quote author="mcosta" date="1364427839"]I see that in your code you never call QThread::quit(); you should do.[/quote]

              It does call QThread::quit. In NetworkWatcher destructor (invoked while destroying Application due to parent-child relationship). QThread::finished is emitted (checkable in debugger). And the thread does quit (seen in debugger). But connected deleteLater are not called!

              I suspect it is because the events loop of main thread and the QThread are already shutdown. But I might be wrong.

              [quote author="mcosta" date="1364427839"]I suggest to launch the Thread in MainWindow constructor or similar and call QThread::quit() on destructor or in "closeEvent"[/quote]

              Calling QThread::quit from QMainWindow::closeEvent should help (if my above guess on the reasons of issues) with destroying and deleting QThread which is owned by the main thread. However it would not help with UDPListener since same conditions still exist for it. But it doesn't go well with the design since NetworkWatcher is bound to application, not some GUI component...

              I will check this (to confirm that UDPListener will still not be destroyed) in few days since I'm on holidays now.

              1 Reply Last reply
              0
              • M Offline
                M Offline
                mcosta
                wrote on last edited by
                #12

                Hi,

                you MUST call QThread::quit in your code and conenct finished() SIGNAL to stop()

                Once your problem is solved don't forget to:

                • Mark the thread as SOLVED using the Topic Tool menu
                • Vote up the answer(s) that helped you to solve the issue

                You can embed images using (http://imgur.com/) or (http://postimage.org/)

                1 Reply Last reply
                0
                • A Offline
                  A Offline
                  Adam Badura
                  wrote on last edited by
                  #13

                  [quote author="KA51O" date="1364456687"]I would add one more connect statement and add a slot on your UDPListener to stop him working.[/quote]

                  Interesting. But who is to call it and when? Or should I call that function instead of calling quit in the NetworkWatcher destructor? That might actually work. But I will be able to check this in few days since I'm on holidays now.

                  [quote author="KA51O" date="1364456687"]The while loop might be a problem though, because as long as you're in that loop control will not return to the event queue and therefor the stop() slot will not be called. I'm not sure how long your object usually spends in that loop. If it spends a lot of time in that loop you might have to think of a way to replace that loop by a signal slot connection. Maybe by connecting your datagramReceived(...) signal to your processDatagrams() slot?[/quote]

                  Yeah... I don't like that loop at all. "The other thread":http://qt-project.org/forums/viewthread/26147/#119546 is all about that loop and getting rid of it. But I think that in my actual case this will likely not be a real issue since the datagrams are not frequent enough. (At least in developer's environment, not sure how about client's environment.)

                  1 Reply Last reply
                  0
                  • K Offline
                    K Offline
                    KA51O
                    wrote on last edited by
                    #14

                    [quote author="Adam Badura" date="1364460931"][quote author="KA51O" date="1364456687"]I would add one more connect statement and add a slot on your UDPListener to stop him working.[/quote]

                    Interesting. But who is to call it and when? Or should I call that function instead of calling quit in the NetworkWatcher destructor? That might actually work. But I will be able to check this in few days since I'm on holidays now.

                    [quote author="KA51O" date="1364456687"]The while loop might be a problem though, because as long as you're in that loop control will not return to the event queue and therefor the stop() slot will not be called. I'm not sure how long your object usually spends in that loop. If it spends a lot of time in that loop you might have to think of a way to replace that loop by a signal slot connection. Maybe by connecting your datagramReceived(...) signal to your processDatagrams() slot?[/quote]

                    Yeah... I don't like that loop at all. "The other thread":http://qt-project.org/forums/viewthread/26147/#119546 is all about that loop and getting rid of it. But I think that in my actual case this will likely not be a real issue since the datagrams are not frequent enough. (At least in developer's environment, not sure how about client's environment.)[/quote]

                    The stop() slot can be called from whereever you want, thats the benefit of sticking to signal slot connections when using QThread. I guess in your case the NetworkWatcher destructor is a good place, but you cannot call the slot directly. Either emit a signal that is connected to the stop slot or use the QMetaObject::invokeMethod() method.

                    The whole loop thing can be a problem in QThreads, because they can block the event loop. And if you rely on the event loop (as for example with your stop() slot) you should avoid busy loops. I usually try to solve the whole loop problem by using a sort of signal slot loop with boolean flags for stop, pause or whatever. This way you return to the event loop after every iteration of your "loop" and thus allow other signals from outside to be handled.

                    1 Reply Last reply
                    0
                    • A Offline
                      A Offline
                      Adam Badura
                      wrote on last edited by
                      #15

                      Along your suggestion I change the code to following:
                      @#include <QtCore/QByteArray>
                      #include <QtCore/QDebug>
                      #include <QtCore/QObject>
                      #include <QtCore/QThread>
                      #include <QtCore/QtGlobal>
                      #include <QtNetwork/QHostAddress>
                      #include <QtNetwork/QNetworkInterface>
                      #include <QtNetwork/QUdpSocket>
                      #include <QtWidgets/QApplication>
                      #include <QtWidgets/QMainWindow>

                      // Required to use QHostAddress as parameter in signals.
                      Q_DECLARE_METATYPE( QHostAddress )

                      ////////////////////////////////////////////////////////////////////////////////

                      class UDPListener
                      : public QObject
                      {
                      Q_OBJECT

                      public:
                      UDPListener( QObject* parent = nullptr )
                      : QObject( parent ),
                      theSocketPtr( new QUdpSocket( this ) ),
                      theShouldStop( false ),
                      theFinishedEmitted( false )
                      {
                      // Required to use QHostAddress as parameter in signals.
                      qRegisterMetaType< QHostAddress >();

                      if (
                        !theSocketPtr->bind(
                          QHostAddress::AnyIPv4,
                          21212,
                          QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint
                        )
                      )
                      {
                        Q_ASSERT( false );
                      }
                      
                      Q_ASSERT( theSocketPtr->state() == QAbstractSocket::BoundState );
                      
                      if ( !theSocketPtr->joinMulticastGroup( QHostAddress( "224.0.0.66" ) ) )
                      {
                        Q_ASSERT( false );
                      }
                      

                      }

                      virtual ~UDPListener()
                      {
                      qDebug() << "UDPListener::~UDPListener()";
                      }

                      public slots:
                      void run()
                      {
                      connect(
                      theSocketPtr,
                      &QUdpSocket::readyRead,
                      this,
                      &UDPListener::processDatagrams
                      );

                      processDatagrams();
                      

                      }

                      void stop()
                      {
                      theShouldStop = true;
                      }

                      private slots:
                      void processDatagram()
                      {
                      QByteArray datagram;
                      QHostAddress senderAddres;
                      quint16 senderPort;

                      datagram.resize( theSocketPtr->pendingDatagramSize() );
                      theSocketPtr->readDatagram(
                        datagram.data(),
                        datagram.size(),
                        &senderAddres,
                        &senderPort
                      );
                      
                      emit datagramReceived( datagram, senderAddres, senderPort );
                      

                      }

                      void processDatagrams()
                      {
                      if ( theFinishedEmitted )
                      return;

                      while ( theSocketPtr->hasPendingDatagrams() && !theShouldStop )
                        processDatagram();
                      
                      if ( theShouldStop )
                      {
                        emit finished();
                        theFinishedEmitted = true;
                      }
                      

                      }

                      signals:
                      void datagramReceived(
                      const QByteArray& aDatagram,
                      const QHostAddress& aSenderAddress,
                      quint16 aSenderPort
                      );

                      void finished();

                      private:
                      QUdpSocket* theSocketPtr;

                      bool theShouldStop;

                      bool theFinishedEmitted;

                      };

                      ////////////////////////////////////////////////////////////////////////////////

                      class NetworkWatcher
                      : public QObject
                      {
                      Q_OBJECT

                      public:
                      NetworkWatcher( QObject* parent = nullptr )
                      : QObject( parent ),
                      theUDPListenerThreadPtr(),
                      theUDPListenerPtr()
                      {
                      }

                      virtual ~NetworkWatcher()
                      {
                      qDebug() << "NetworkWatcher::~NetworkWatcher()";

                      QMetaObject::invokeMethod(
                        theUDPListenerPtr,
                        "stop",
                        Qt::QueuedConnection
                      );
                      //theUDPListenerThreadPtr->quit();
                      

                      }

                      void runOnNewThread()
                      {
                      theUDPListenerPtr = new UDPListener();
                      connect(
                      theUDPListenerPtr,
                      &UDPListener::datagramReceived,
                      this,
                      &NetworkWatcher::onDatagramReceived
                      );

                      theUDPListenerThreadPtr = new QThread();
                      theUDPListenerThreadPtr->setObjectName( "NetworkWatcher::UDPListener" );
                      
                      connect(
                        theUDPListenerPtr,
                        &UDPListener::finished,
                        theUDPListenerThreadPtr,
                        &QThread::quit
                      );
                      
                      connect(
                        theUDPListenerThreadPtr,
                        &QThread::started,
                        theUDPListenerPtr,
                        &UDPListener::run
                      );
                      connect(
                        theUDPListenerThreadPtr,
                        &QThread::finished,
                        theUDPListenerPtr,
                        &UDPListener::deleteLater
                      );
                      connect(
                        theUDPListenerThreadPtr,
                        &QThread::finished,
                        theUDPListenerThreadPtr,
                        &QThread::deleteLater
                      );
                      
                      theUDPListenerPtr->moveToThread( theUDPListenerThreadPtr );
                      theUDPListenerThreadPtr->start();
                      

                      }

                      protected slots:
                      void onDatagramReceived(
                      const QByteArray& aDatagram,
                      const QHostAddress& aSenderAddress,
                      quint16 aSenderPort
                      )
                      {
                      /* ... */
                      }

                      private:
                      QThread* theUDPListenerThreadPtr;

                      UDPListener* theUDPListenerPtr;

                      };

                      ////////////////////////////////////////////////////////////////////////////////

                      class Application
                      : public QApplication
                      {
                      Q_OBJECT

                      public:
                      Application( int& argc, char** argv )
                      : QApplication( argc, argv ),
                      theNetworkWatcherPtr( new NetworkWatcher( this ) )
                      {
                      theNetworkWatcherPtr->runOnNewThread();
                      }

                      virtual ~Application()
                      {
                      qDebug() << "Application::~Application()";
                      }

                      private:
                      NetworkWatcher* theNetworkWatcherPtr;

                      };

                      ////////////////////////////////////////////////////////////////////////////////

                      int main( int argc, char* argv[] )
                      {
                      Application application( argc, argv );

                      QMainWindow mainWindow;
                      mainWindow.show();

                      return application.exec();
                      }@
                      But still UDPListener destructor is not called.

                      1 Reply Last reply
                      0
                      • JKSHJ Offline
                        JKSHJ Offline
                        JKSH
                        Moderators
                        wrote on last edited by
                        #16

                        Your code relies on this order of calling/signalling:

                        Application::~Application() calls NetworkWatcher::~NetworkWatcher() via parent-child relationship

                        NetworkWatcher::~NetworkWatcher() invokes UDPListener::stop() [queued]

                        UDPListener::stop() sets theShouldStop = true

                        UDPListener::processDatagrams() checks theShouldStop, emits UDPListener::finished()

                        UDPListener::finished() invokes QThread::quit() [queued]

                        QThread::quit() emits QThread::finished()

                        QThread::finished() invokes UDPListener::deleteLater() and QThread::deleteLater() [queued]

                        The problem is, you've already terminated the main event loop in step #1. Thus, step #5 cannot happen; theUDPListenerThreadPtr lives in the main thread, so its slots can no longer be invoked after Application::~Application() returns.

                        Solution: Do not use Application::~Application() to trigger cleanup. Instead, the application should only be allowed to quit AFTER all cleanup has been completed -- e.g. connect QThread::deleted() to QApplication::quit(), and then clean up everything when the main window is closed.

                        Also, there's no need to subclass QApplication -- just make the NetworkWatcher a local variable/object in main().

                        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                        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