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. How to ensure all connected classes react to the signal from different threads in the same order?

How to ensure all connected classes react to the signal from different threads in the same order?

Scheduled Pinned Locked Moved Solved General and Desktop
7 Posts 4 Posters 201 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
    AlexpoUA
    wrote on last edited by
    #1

    Hi, so I made a small PoC where I connected two classes A and B to the third one C (they all live in the same main thread). I made C launch a bunch of threads that continuously emit signals with a number from 1 to 1000 from the FOR loop (threads are not synchronized). Then I compared the A's and B's arrays of received signals. The order of received signals wasn't aligned. So in order to ensure that all classes will receive signals in the same order should I just add synchronization to the C's emit?
    The code for better understanding:

    // a.h
    class A : public QObject
    {
        Q_OBJECT
    public:
        explicit A(QObject* parent)
            : QObject(parent)
        {
        }
    
    public slots:
        void reactToSignal(int number)
        {
            // simulate work
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
            m_nums.push_back(number);
        }
    public:
        std::vector<int> m_nums;
    };
    
    //b.h is the same as a.h
    
    // c.h
    class C : public QObject
    {
        Q_OBJECT
    public:
        explicit C(QObject *parent = nullptr)
            : QObject(parent)
        {
        }
    
        ~C()
        {
            for(std::thread& worker : m_threads)
            {
                if(worker.joinable())
                    worker.join();
            }
        }
    
    void StartWorking()
        {
            int size = 4;
            for(int i = 0; i < size; i++)
            {
                m_threads.emplace_back(std::thread(std::bind(&C::Work, this)));
            }
    
            m_isReady = true;
            m_cv.notify_all();
        }
    
    signals:
        void statusChanged(int);
    
    private:
        void Work()
        {
            {
                std::shared_lock<std::shared_mutex> lock(m_mutex);
                m_cv.wait(lock,[this]{return m_isReady;});
            }
    
            for(int i = 1; i <= 1000; i++)
            {
                emit statusChanged(i);
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
            }
        }
    
    private:
        bool m_isReady = false;
        std::condition_variable_any m_cv;
        std::shared_mutex m_mutex;
        std::vector<std::thread> m_threads;
    };
    

    Then I just connect them like that in main window:

        m_a = new A(this);
        m_b = new B(this);
        m_c = new C(this);
    
        isConnected = isConnected && connect(m_c, SIGNAL(statusChanged(int)), m_a, SLOT(reactToSignal(int)));
        isConnected = isConnected && connect(m_c, SIGNAL(statusChanged(int)), m_b, SLOT(reactToSignal(int)));
        
        m_c->StartWorking();
    

    When I compare A's and B's m_nums vectors, they don't align (in most cases), which means signals were processed in different times for A and B. How to ensure that each signal is processed in the same order for A and B?

    jsulmJ 1 Reply Last reply
    0
    • A AlexpoUA

      @Christian-Ehrlicher What I want is to make classes connected to statusChanged(int number) receive the numbers in the same order, example:
      thread1 emits statusChanged(1)
      thread2 emits statusChanged(1)
      thread3 emits statusChanged(1)
      thread1 emits statusChanged(2)
      thread2 emits statusChanged(2)

      // there is no order in which threads emit signals, it's ok
      // signals are then transformed into events and are added to the main thread's event loop. I want to achieve the following:

      If A receives signals in order of 1,1,1,2,2 I want B to also receive them in such order.
      A may receive them in order of 1,1,2,1,2, and again I want B to receive those signals in the same order.
      Should I just synchronize access to emit in C?

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

      @AlexpoUA
      Would you not be better/simpler/quicker to place just a single slot on the signal and have that (directly) call whatever in classes A, B, ... , so now you receive one signal in one slot rather than separate connect()s?

      Note that being across threads you still do not what order signals arrive for slot processing. However, I believe that if a given thread emits signal and then shortly afterward emits the same signal again then they will arrive in that order for the slot calls. Unless an expert says even that is not guaranteed?

      Otherwise as you say you will have to put in some sort of synchronisation. Which would seem like it spoils the point of using threads.

      A 1 Reply Last reply
      0
      • A AlexpoUA

        Hi, so I made a small PoC where I connected two classes A and B to the third one C (they all live in the same main thread). I made C launch a bunch of threads that continuously emit signals with a number from 1 to 1000 from the FOR loop (threads are not synchronized). Then I compared the A's and B's arrays of received signals. The order of received signals wasn't aligned. So in order to ensure that all classes will receive signals in the same order should I just add synchronization to the C's emit?
        The code for better understanding:

        // a.h
        class A : public QObject
        {
            Q_OBJECT
        public:
            explicit A(QObject* parent)
                : QObject(parent)
            {
            }
        
        public slots:
            void reactToSignal(int number)
            {
                // simulate work
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
                m_nums.push_back(number);
            }
        public:
            std::vector<int> m_nums;
        };
        
        //b.h is the same as a.h
        
        // c.h
        class C : public QObject
        {
            Q_OBJECT
        public:
            explicit C(QObject *parent = nullptr)
                : QObject(parent)
            {
            }
        
            ~C()
            {
                for(std::thread& worker : m_threads)
                {
                    if(worker.joinable())
                        worker.join();
                }
            }
        
        void StartWorking()
            {
                int size = 4;
                for(int i = 0; i < size; i++)
                {
                    m_threads.emplace_back(std::thread(std::bind(&C::Work, this)));
                }
        
                m_isReady = true;
                m_cv.notify_all();
            }
        
        signals:
            void statusChanged(int);
        
        private:
            void Work()
            {
                {
                    std::shared_lock<std::shared_mutex> lock(m_mutex);
                    m_cv.wait(lock,[this]{return m_isReady;});
                }
        
                for(int i = 1; i <= 1000; i++)
                {
                    emit statusChanged(i);
                    std::this_thread::sleep_for(std::chrono::milliseconds(1));
                }
            }
        
        private:
            bool m_isReady = false;
            std::condition_variable_any m_cv;
            std::shared_mutex m_mutex;
            std::vector<std::thread> m_threads;
        };
        

        Then I just connect them like that in main window:

            m_a = new A(this);
            m_b = new B(this);
            m_c = new C(this);
        
            isConnected = isConnected && connect(m_c, SIGNAL(statusChanged(int)), m_a, SLOT(reactToSignal(int)));
            isConnected = isConnected && connect(m_c, SIGNAL(statusChanged(int)), m_b, SLOT(reactToSignal(int)));
            
            m_c->StartWorking();
        

        When I compare A's and B's m_nums vectors, they don't align (in most cases), which means signals were processed in different times for A and B. How to ensure that each signal is processed in the same order for A and B?

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

        @AlexpoUA said in How to ensure all connected classes react to the signal from different threads in the same order?:

        The order of received signals wasn't aligned

        What does this mean?

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

        A 1 Reply Last reply
        0
        • jsulmJ jsulm

          @AlexpoUA said in How to ensure all connected classes react to the signal from different threads in the same order?:

          The order of received signals wasn't aligned

          What does this mean?

          A Offline
          A Offline
          AlexpoUA
          wrote on last edited by
          #3

          @jsulm what I meant is
          A's m_nums is like {1,1,1,1,2,2,3,2,...}
          and B's m_nums is {1,1,1,1,2,3,2,2,...}.
          Different order of received numbers from emitted signals.

          1 Reply Last reply
          0
          • Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #4

            There is no order as soon as there are different threads involved - how should this work? How to you want to handle which thread is executed first by the os?

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

            A 1 Reply Last reply
            1
            • Christian EhrlicherC Christian Ehrlicher

              There is no order as soon as there are different threads involved - how should this work? How to you want to handle which thread is executed first by the os?

              A Offline
              A Offline
              AlexpoUA
              wrote on last edited by AlexpoUA
              #5

              @Christian-Ehrlicher What I want is to make classes connected to statusChanged(int number) receive the numbers in the same order, example:
              thread1 emits statusChanged(1)
              thread2 emits statusChanged(1)
              thread3 emits statusChanged(1)
              thread1 emits statusChanged(2)
              thread2 emits statusChanged(2)

              // there is no order in which threads emit signals, it's ok
              // signals are then transformed into events and are added to the main thread's event loop. I want to achieve the following:

              If A receives signals in order of 1,1,1,2,2 I want B to also receive them in such order.
              A may receive them in order of 1,1,2,1,2, and again I want B to receive those signals in the same order.
              Should I just synchronize access to emit in C?

              JonBJ 1 Reply Last reply
              0
              • A AlexpoUA

                @Christian-Ehrlicher What I want is to make classes connected to statusChanged(int number) receive the numbers in the same order, example:
                thread1 emits statusChanged(1)
                thread2 emits statusChanged(1)
                thread3 emits statusChanged(1)
                thread1 emits statusChanged(2)
                thread2 emits statusChanged(2)

                // there is no order in which threads emit signals, it's ok
                // signals are then transformed into events and are added to the main thread's event loop. I want to achieve the following:

                If A receives signals in order of 1,1,1,2,2 I want B to also receive them in such order.
                A may receive them in order of 1,1,2,1,2, and again I want B to receive those signals in the same order.
                Should I just synchronize access to emit in C?

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

                @AlexpoUA
                Would you not be better/simpler/quicker to place just a single slot on the signal and have that (directly) call whatever in classes A, B, ... , so now you receive one signal in one slot rather than separate connect()s?

                Note that being across threads you still do not what order signals arrive for slot processing. However, I believe that if a given thread emits signal and then shortly afterward emits the same signal again then they will arrive in that order for the slot calls. Unless an expert says even that is not guaranteed?

                Otherwise as you say you will have to put in some sort of synchronisation. Which would seem like it spoils the point of using threads.

                A 1 Reply Last reply
                0
                • JonBJ JonB

                  @AlexpoUA
                  Would you not be better/simpler/quicker to place just a single slot on the signal and have that (directly) call whatever in classes A, B, ... , so now you receive one signal in one slot rather than separate connect()s?

                  Note that being across threads you still do not what order signals arrive for slot processing. However, I believe that if a given thread emits signal and then shortly afterward emits the same signal again then they will arrive in that order for the slot calls. Unless an expert says even that is not guaranteed?

                  Otherwise as you say you will have to put in some sort of synchronisation. Which would seem like it spoils the point of using threads.

                  A Offline
                  A Offline
                  AlexpoUA
                  wrote on last edited by
                  #7

                  @JonB actually it sounds like a good idea, thanks

                  1 Reply Last reply
                  0
                  • A AlexpoUA 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