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. Communication between multiple threads that run at different frequencies
QtWS25 Last Chance

Communication between multiple threads that run at different frequencies

Scheduled Pinned Locked Moved Solved General and Desktop
30 Posts 5 Posters 5.4k 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.
  • J JonB
    30 Aug 2022, 13:03

    @Axel-Spoerl
    Totally respect your answer. I have seen mentions of QMetaObject.invokeMethod() on the web, but I know it's used in "advanced" situations (including a lot from Python/PyQt/PySide, probably understandably) so haven't touched it! I can tell that from the 'stop' argument being a literal string it's going to look up the method to call by name ("reflection"), like the old SIGNAL/SLOT() macros approach did; so being more a C++ purist I would tend to avoid that, preferring compile-time safety/argument checking etc.

    A Online
    A Online
    Axel Spoerl
    Moderators
    wrote on 30 Aug 2022, 13:23 last edited by
    #16

    @JonB
    Totally fair point! A kitten dies, each time you resolve a symbol with a string search at run time....in Python.
    There are more invokeMethod() overloads for the C++ lovers from 6.4 onward.

    Software Engineer
    The Qt Company, Oslo

    J 1 Reply Last reply 30 Aug 2022, 14:18
    1
    • A Axel Spoerl
      30 Aug 2022, 13:23

      @JonB
      Totally fair point! A kitten dies, each time you resolve a symbol with a string search at run time....in Python.
      There are more invokeMethod() overloads for the C++ lovers from 6.4 onward.

      J Offline
      J Offline
      JonB
      wrote on 30 Aug 2022, 14:18 last edited by
      #17

      @Axel-Spoerl said in Communication between multiple threads that run at different frequencies:

      A kitten dies, each time you resolve a symbol with a string search at run time....in Python.

      :)

      1 Reply Last reply
      0
      • E Offline
        E Offline
        EvheMary
        wrote on 30 Aug 2022, 19:01 last edited by
        #18

        Thanks for all these comments. I might not fully understand since I'm still a beginner in qt, so sorry if I mentioned something wrong. I thought that since I use a signal to communicate with the Qtimer thread it should be safe. And I should be calling the stop method from the thread that it was created. Is my implementation of the signals and slot not correct?
        my understanding of using the signal and slot is :
        UI Thread --> send signal --> stop method (slot) at another thread --> QTimer stop

        J 1 Reply Last reply 30 Aug 2022, 22:23
        0
        • E EvheMary
          30 Aug 2022, 19:01

          Thanks for all these comments. I might not fully understand since I'm still a beginner in qt, so sorry if I mentioned something wrong. I thought that since I use a signal to communicate with the Qtimer thread it should be safe. And I should be calling the stop method from the thread that it was created. Is my implementation of the signals and slot not correct?
          my understanding of using the signal and slot is :
          UI Thread --> send signal --> stop method (slot) at another thread --> QTimer stop

          J Offline
          J Offline
          JonB
          wrote on 30 Aug 2022, 22:23 last edited by
          #19

          @EvheMary
          You call self.worker.stop() (which calls self.timer.stop()) directly from the UI thread.

          1 Reply Last reply
          1
          • E Offline
            E Offline
            EvheMary
            wrote on 31 Aug 2022, 06:26 last edited by EvheMary
            #20

            @JonB But if i call self.worker.stop() that calls self.timer.stop() from the UI Thread, the error QObject::~QObject: Timers cannot be stopped from another thread appears and that's what I want to know what causes it and how to remove it. If i change self.timer.stop() with QMetaObject.invokeMethod(self.timer, 'stop', Qt.AutoConnection) the error don't appear.

            J J 2 Replies Last reply 31 Aug 2022, 06:54
            0
            • E EvheMary
              31 Aug 2022, 06:26

              @JonB But if i call self.worker.stop() that calls self.timer.stop() from the UI Thread, the error QObject::~QObject: Timers cannot be stopped from another thread appears and that's what I want to know what causes it and how to remove it. If i change self.timer.stop() with QMetaObject.invokeMethod(self.timer, 'stop', Qt.AutoConnection) the error don't appear.

              J Offline
              J Offline
              jsulm
              Lifetime Qt Champion
              wrote on 31 Aug 2022, 06:54 last edited by
              #21

              @EvheMary said in Communication between multiple threads that run at different frequencies:

              that's what I want to know what causes it

              Your worker object lives in another thread. So, if you call self.worker.stop() in UI thread stop() will be executed in UI thread and it will also call self.timer.stop() in UI thread. But self.timer also lives in the worker thread, so you get that warning (QTimer is not thread safe).
              To avoid this you should NOT call self.worker.stop() directly from the UI thread but instead either use invokeMethod() or connect self.worker.stop() to a signal in your UI and emit this signal to stop the worker.

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

              1 Reply Last reply
              1
              • E Offline
                E Offline
                EvheMary
                wrote on 31 Aug 2022, 07:37 last edited by
                #22

                @jsulm Yes. Thanks for the answer. Now I know that calling method in another thread from the UI thread is not good. But I don't know why when I connect a signal from UI Thread to the self.worker.stop(), the error persists. It will only be gone when I use invokeMethod() instead of self.timer.stop(), which leads to why I suspect QTimer to be another threading interface (because I need to send another signal to the timer itself).

                J 1 Reply Last reply 31 Aug 2022, 07:59
                0
                • E EvheMary
                  31 Aug 2022, 07:37

                  @jsulm Yes. Thanks for the answer. Now I know that calling method in another thread from the UI thread is not good. But I don't know why when I connect a signal from UI Thread to the self.worker.stop(), the error persists. It will only be gone when I use invokeMethod() instead of self.timer.stop(), which leads to why I suspect QTimer to be another threading interface (because I need to send another signal to the timer itself).

                  J Offline
                  J Offline
                  jsulm
                  Lifetime Qt Champion
                  wrote on 31 Aug 2022, 07:59 last edited by
                  #23

                  @EvheMary said in Communication between multiple threads that run at different frequencies:

                  But I don't know why when I connect a signal from UI Thread to the self.worker.stop()

                  Can you show how you did the connection?

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

                  1 Reply Last reply
                  0
                  • A Online
                    A Online
                    Axel Spoerl
                    Moderators
                    wrote on 31 Aug 2022, 08:00 last edited by
                    #24

                    Adding to @jsulm :

                    what causes it

                    Theoretically, two different threads could call the QTimer's stop()method at the same time. Or, even worse, one of them calls start().
                    The warning is triggered, if a slot is called from a thread different from the timer's living environment.
                    Emitting a signal or calling invokeMethod() makes sure that concurrent calls are properly serialized, posted into the timer's event loop and executed within the timer's thread. Such behaviour is not QTimerspecific. It applies to most Qt classes, check this for more information.

                    Software Engineer
                    The Qt Company, Oslo

                    1 Reply Last reply
                    1
                    • E EvheMary
                      31 Aug 2022, 06:26

                      @JonB But if i call self.worker.stop() that calls self.timer.stop() from the UI Thread, the error QObject::~QObject: Timers cannot be stopped from another thread appears and that's what I want to know what causes it and how to remove it. If i change self.timer.stop() with QMetaObject.invokeMethod(self.timer, 'stop', Qt.AutoConnection) the error don't appear.

                      J Offline
                      J Offline
                      JonB
                      wrote on 31 Aug 2022, 08:29 last edited by
                      #25

                      @EvheMary said in Communication between multiple threads that run at different frequencies:

                      @JonB But if i call self.worker.stop() that calls self.timer.stop() from the UI Thread, the error QObject::~QObject: Timers cannot be stopped from another thread appears and that's what I want to know what causes it and how to remove it.

                      That is precisely what I explained: you call self.timer.stop() from the UI Thread, that is not the thread where the timer lives, hence the explicit error message telling you what is wrong.

                      But I don't know why when I connect a signal from UI Thread to the self.worker.stop(), the error persists.

                      As @jsulm said, show your connect() statement for this.

                      1 Reply Last reply
                      0
                      • E Offline
                        E Offline
                        EvheMary
                        wrote on 31 Aug 2022, 08:33 last edited by EvheMary
                        #26

                        I actually updated my code since i also asks similar question in stacks overflow (though still without solution). I'll show the important parts only:

                        class Window(QWidget):
                        
                            startSig = Signal()
                            stopSig = Signal()
                        
                            def __init__(self):
                                        self.button.clicked.connect(self.startThr)
                                        self.button2.clicked.connect(self.stopThr)    # I called the stop method (for the UI Thread) here
                                        self.button3.clicked.connect(self.showDlg)
                        
                            def startThr(self):
                                        if self.thread is None or not self.thread.isRunning():
                                        self.thread = QThread()
                                        self.worker = Inlet_Worker()
                                        self.worker.moveToThread(self.thread)
                                        self.worker.data.connect(self.dlg.update)
                                        self.worker.tabs.connect(self.switch_tab)
                                        self.stopSig.connect(self.worker.stop)    # I connect the signal here
                                        self.worker.finish.connect(self.finished)
                                        self.thread.started.connect(self.worker.starter)
                                        self.thread.start()
                        
                            def stopThr(self):
                                    self.stopSig.emit()    # Emit stop signal
                        

                        And here is the thread

                        class Inlet_Worker(QObject):
                        
                            data = Signal(int)
                            tabs = Signal(int)
                            finish = Signal()
                        
                            def __init__(self):
                                super().__init__()
                                self._stopped = False
                                self._registered = False
                                self.init_timers()
                                self.c = 0
                                self.d = 0
                        
                            def init_timers(self):
                                self.timer1 = QTimer(self)
                                self.timer1.timeout.connect(self.routine)
                                self.timer2 = QTimer(self)
                                self.timer2.timeout.connect(self.routine2)
                        
                            def starter(self):
                                self.timer1.start(1000)
                                self.timer2.start(2000)
                        
                            def routine(self):
                                self.data.emit(self.c)
                                self.c += 1
                                
                            def routine2(self):
                                self.tabs.emit(self.d)
                                self.d += 1
                        
                            @Slot()
                            def stop(self):
                                self.timer1.stop()
                                self.timer2.stop()
                                print('stopped')
                                self.finish.emit()
                        

                        The stop method in UI Thread emit signal
                        I connect the signal to the self.timer.stop() in the thread
                        I can upload the full code if necessary.

                        J 1 Reply Last reply 31 Aug 2022, 08:41
                        0
                        • E EvheMary
                          31 Aug 2022, 08:33

                          I actually updated my code since i also asks similar question in stacks overflow (though still without solution). I'll show the important parts only:

                          class Window(QWidget):
                          
                              startSig = Signal()
                              stopSig = Signal()
                          
                              def __init__(self):
                                          self.button.clicked.connect(self.startThr)
                                          self.button2.clicked.connect(self.stopThr)    # I called the stop method (for the UI Thread) here
                                          self.button3.clicked.connect(self.showDlg)
                          
                              def startThr(self):
                                          if self.thread is None or not self.thread.isRunning():
                                          self.thread = QThread()
                                          self.worker = Inlet_Worker()
                                          self.worker.moveToThread(self.thread)
                                          self.worker.data.connect(self.dlg.update)
                                          self.worker.tabs.connect(self.switch_tab)
                                          self.stopSig.connect(self.worker.stop)    # I connect the signal here
                                          self.worker.finish.connect(self.finished)
                                          self.thread.started.connect(self.worker.starter)
                                          self.thread.start()
                          
                              def stopThr(self):
                                      self.stopSig.emit()    # Emit stop signal
                          

                          And here is the thread

                          class Inlet_Worker(QObject):
                          
                              data = Signal(int)
                              tabs = Signal(int)
                              finish = Signal()
                          
                              def __init__(self):
                                  super().__init__()
                                  self._stopped = False
                                  self._registered = False
                                  self.init_timers()
                                  self.c = 0
                                  self.d = 0
                          
                              def init_timers(self):
                                  self.timer1 = QTimer(self)
                                  self.timer1.timeout.connect(self.routine)
                                  self.timer2 = QTimer(self)
                                  self.timer2.timeout.connect(self.routine2)
                          
                              def starter(self):
                                  self.timer1.start(1000)
                                  self.timer2.start(2000)
                          
                              def routine(self):
                                  self.data.emit(self.c)
                                  self.c += 1
                                  
                              def routine2(self):
                                  self.tabs.emit(self.d)
                                  self.d += 1
                          
                              @Slot()
                              def stop(self):
                                  self.timer1.stop()
                                  self.timer2.stop()
                                  print('stopped')
                                  self.finish.emit()
                          

                          The stop method in UI Thread emit signal
                          I connect the signal to the self.timer.stop() in the thread
                          I can upload the full code if necessary.

                          J Offline
                          J Offline
                          jsulm
                          Lifetime Qt Champion
                          wrote on 31 Aug 2022, 08:41 last edited by
                          #27

                          @EvheMary said in Communication between multiple threads that run at different frequencies:

                          self.stopSig.connect(self.worker.stop)

                          Maybe you need explicetly define Qt::QueuedConnection (in C++ it is default for connections across threads).

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

                          E 1 Reply Last reply 31 Aug 2022, 09:33
                          1
                          • J jsulm
                            31 Aug 2022, 08:41

                            @EvheMary said in Communication between multiple threads that run at different frequencies:

                            self.stopSig.connect(self.worker.stop)

                            Maybe you need explicetly define Qt::QueuedConnection (in C++ it is default for connections across threads).

                            E Offline
                            E Offline
                            EvheMary
                            wrote on 31 Aug 2022, 09:33 last edited by
                            #28

                            @jsulm Oh wow, this also works. Unfortunately, I have already marked the previous answer as the solution but this works too. It seems that the default type for connect in pyqt is Qt.AutoConnection. Do you mind explaining why this happens when using AutoConnection while it works with queuedConnection?

                            J 1 Reply Last reply 31 Aug 2022, 09:41
                            0
                            • E EvheMary
                              31 Aug 2022, 09:33

                              @jsulm Oh wow, this also works. Unfortunately, I have already marked the previous answer as the solution but this works too. It seems that the default type for connect in pyqt is Qt.AutoConnection. Do you mind explaining why this happens when using AutoConnection while it works with queuedConnection?

                              J Offline
                              J Offline
                              jsulm
                              Lifetime Qt Champion
                              wrote on 31 Aug 2022, 09:41 last edited by
                              #29

                              @EvheMary AutoConnection should be fine (it is also default in C++): Qt decides what to use. So, in case of connections across threads Qt uses QueuedConnection then. But in your case it looks like this is not happening, I don't know why. Maybe there are differences in PyQt.

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

                              1 Reply Last reply
                              0
                              • A Online
                                A Online
                                Axel Spoerl
                                Moderators
                                wrote on 31 Aug 2022, 13:26 last edited by
                                #30

                                AutoConnectionshould connect

                                • directly (synchronously) when object and caller live in the same thread.
                                • queued when object and caller live in different threads.

                                In C++ that can never go wrong. I am not a Python guru (repeating myself now). Maybe the explanation is that the connection type gets stuck in DirectConnection when the signal is connected before the second thread is being detached. In any case there's nothing wrong about explicitly using QueuedConnection.

                                Software Engineer
                                The Qt Company, Oslo

                                1 Reply Last reply
                                0

                                25/30

                                31 Aug 2022, 08:29

                                • Login

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