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. signals and slots across threads without QThread inheritance

signals and slots across threads without QThread inheritance

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 6 Posters 2.7k 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.
  • D dan13l

    Thank you @KroMignon for your reply.
    Uh, of course, I do not expect anything, you are right. Stupid mistake from my side when creating this example as condensed version of my original code.

    Of course, I have to add this to main.cxx:

    Object::connect(&b, SIGNAL(updater(int)), &a, SLOT(makeUpdate(int)), Qt::AutoConnection);
    
    D Offline
    D Offline
    dan13l
    wrote on last edited by
    #5
    This post is deleted!
    1 Reply Last reply
    0
    • KroMignonK KroMignon

      @dan13l said in signals and slots across threads without QThread inheritance:

      Object::connect(&b, SIGNAL(updater(int)), &a, SLOT(makeUpdate(int)), Qt::AutoConnection);

      • First: I would also suggest you to use new Qt connect syntax, to enable connection check at build time
      • Second: always use emit when calling a signal, this made your code easier to understand
      • Third: I would suggest you to use QueuedConnection to ensure signal will be emitted when event loop is running
      Object::connect(&b, &B::updater , &a, &A::makeUpdate, Qt::QueuedConnection);
      
      D Offline
      D Offline
      dan13l
      wrote on last edited by
      #6

      @KroMignon Awesome!!
      Thank you so much for your help! I really appreciate that! =)

      Point one and three of your suggestion did the job!
      So, what I learned is to explicitly set QueuedConnection together with the new syntax, because I tried again AutoConnection and then it fails to work.

      Just as a documentation, below are the corrected and now working code lines:

      main.cxx

      #include <QApplication>
      #include <QThread>
      #include <iostream>
      #include "A.h"
      #include "B.h"
      int main(int argc , char *argv[]){
        QApplication app(argc, argv);
        //doing something for gui and user ...
        // ...
        std::cout<<"Hello Qt Forum."<<std::endl;
        // ...
        A a(1);
        B b(2);
        //creating thread 
        QThread* thread = new QThread;
        a.moveToThread(thread); // change thread affinity
        bool check1, check2;
        check1 = QObject::connect(thread, &QThread::started, &a, &A::processList, Qt::QueuedConnection);
        check2 = QObject::connect(&b, &B::updater , &a, &A::makeUpdate, Qt::QueuedConnection);
        std::cout<<"successful connection?: check1= "<<check1<<" and check2= "<<check2<<std::endl;
      
        //starting thread
        thread->start();
        
        //now: give an update from B to A:
        emit b.updater(42); // --> now it is working =)
        return app.exec();
      }
      
      

      corrected A.cxx (excerpt)

      ...
      
      void A::processList(){
        int i = 0;
        int q = 0;
        while(running){
          q++;
          if( i < list.size() and !list.isEmpty() ){
            //doing something that should not block main.cxx
            i++;
            if( i == list.size() ){
      	//end of list, reset list index
      	i = 0;
            }
          }
          if( !buffer.isEmpty() ){
            list.append(buffer);
            std::cout<<"Now list size is: "<<list.size()<<" and was added at step "<<q<<std::endl;
            buffer.clear();
            std::cout<<"after cleaning buffer, buffer size is:"<<buffer.size()<<std::endl;
          }
        }
      }
      ...
      
      1 Reply Last reply
      1
      • SGaistS Offline
        SGaistS Offline
        SGaist
        Lifetime Qt Champion
        wrote on last edited by
        #7

        Hi,

        One very important rule with signals and slots: do not emit signal from other classes. You shall only emit from within your class implementation.

        Please take a look at the QThread documentation to see an example of the worker object approach.

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

        D 1 Reply Last reply
        2
        • Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #8

          @dan13l said in signals and slots across threads without QThread inheritance:

          So, what I learned is to explicitly set QueuedConnection together with the new syntax, because I tried again AutoConnection and then it fails to work.

          Then you're doing something wrong and your QObject('s) don't live in the appropriate thread.

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

          D 1 Reply Last reply
          3
          • SGaistS SGaist

            Hi,

            One very important rule with signals and slots: do not emit signal from other classes. You shall only emit from within your class implementation.

            Please take a look at the QThread documentation to see an example of the worker object approach.

            D Offline
            D Offline
            dan13l
            wrote on last edited by
            #9

            @SGaist said in signals and slots across threads without QThread inheritance:

            One very important rule with signals and slots: do not emit signal from other classes. You shall only emit from within your class implementation.

            Hi @SGaist, thanks for that hint, I will try to implement it that way. Seems that for my real code it will be anyway necessary to create an additional object in which the emit can happen then.

            1 Reply Last reply
            0
            • Christian EhrlicherC Christian Ehrlicher

              @dan13l said in signals and slots across threads without QThread inheritance:

              So, what I learned is to explicitly set QueuedConnection together with the new syntax, because I tried again AutoConnection and then it fails to work.

              Then you're doing something wrong and your QObject('s) don't live in the appropriate thread.

              D Offline
              D Offline
              dan13l
              wrote on last edited by
              #10

              @Christian-Ehrlicher said in signals and slots across threads without QThread inheritance:

              Then you're doing something wrong and your QObject('s) don't live in the appropriate thread.

              Hmm...okay, maybe I should investigate that further. Does it make a difference, when I send the QObject to the thread through

              obj.MoveToThread(thread);
              

              and when I create the connection? What I mean is, doing:

              obj.MoveToThread(thread);
              QObject::connect(...);
              

              or doing

              QObject::connect(...);
              
              obj.MoveToThread(thread);
              
              KroMignonK 1 Reply Last reply
              0
              • D dan13l

                @Christian-Ehrlicher said in signals and slots across threads without QThread inheritance:

                Then you're doing something wrong and your QObject('s) don't live in the appropriate thread.

                Hmm...okay, maybe I should investigate that further. Does it make a difference, when I send the QObject to the thread through

                obj.MoveToThread(thread);
                

                and when I create the connection? What I mean is, doing:

                obj.MoveToThread(thread);
                QObject::connect(...);
                

                or doing

                QObject::connect(...);
                
                obj.MoveToThread(thread);
                
                KroMignonK Offline
                KroMignonK Offline
                KroMignon
                wrote on last edited by
                #11

                @dan13l There is no difference if you first move the instance to another thread or connect signals/slots first.

                Have you read the documentation Signals & Slots?

                It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                D 1 Reply Last reply
                2
                • KroMignonK KroMignon

                  @dan13l There is no difference if you first move the instance to another thread or connect signals/slots first.

                  Have you read the documentation Signals & Slots?

                  D Offline
                  D Offline
                  dan13l
                  wrote on last edited by
                  #12

                  @KroMignon okay, I just wanted to give some approach for @Christian-Ehrlicher`s comment that something is still not correct, when AutoConnect does not work. Now I see that my suggestion was indeed not the best one ... πŸ™ˆ

                  kshegunovK KroMignonK 2 Replies Last reply
                  0
                  • D dan13l

                    @KroMignon okay, I just wanted to give some approach for @Christian-Ehrlicher`s comment that something is still not correct, when AutoConnect does not work. Now I see that my suggestion was indeed not the best one ... πŸ™ˆ

                    kshegunovK Offline
                    kshegunovK Offline
                    kshegunov
                    Moderators
                    wrote on last edited by kshegunov
                    #13

                    @dan13l said in signals and slots across threads without QThread inheritance:

                    okay, I just wanted to give some approach for @Christian-Ehrlicher`s comment that something is still not correct

                    What Christian means is that the connection should be agnostic to the object's thread and that if you force the connection types you can hide actual bugs (i.e. an object not in the correct thread) from yourself. The subtler point is that QObject::connect already defaults to the Qt::QueuedConnection iff your object is already in a different thread, while you can see in the stack that it was directly invoked if in fact you have a slot executed before the object's been moved. All in all: do not force the connection type to queued unless you actually need to call the slot in the same thread but later (i.e. through the event loop).

                    Read and abide by the Qt Code of Conduct

                    1 Reply Last reply
                    4
                    • D dan13l

                      @KroMignon okay, I just wanted to give some approach for @Christian-Ehrlicher`s comment that something is still not correct, when AutoConnect does not work. Now I see that my suggestion was indeed not the best one ... πŸ™ˆ

                      KroMignonK Offline
                      KroMignonK Offline
                      KroMignon
                      wrote on last edited by
                      #14

                      @dan13l It could be hard to understand at beginning how signals/slots works. But is a fundamental mechanism in Qt world.
                      The connection type should never be specified (which means it is "automatic").
                      In automatic mode, the connection mode will be decide at signal call:

                      • when source and destination QObject are in same thread, direct connection will be used
                      • when source and destination QObject are in different thread queued connection will be used

                      By using queued connection, a shadow copy of each parameter will be done and used for the slot call. And the call will be registered in event queue from destination thread, executed when entering in the event queue. So it will be delayed.
                      This mechanism can simplify multi-threading communication, but you have to be aware that a copy of each parameter will always be done!

                      In direct connection, the call will be done directly, like a function call without shadow copy.

                      Hope this will help you.

                      It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                      1 Reply Last reply
                      2
                      • S Offline
                        S Offline
                        SimonSchroeder
                        wrote on last edited by
                        #15

                        The first thing I see in the code is that there is not possibility for A to further process any events.

                        If you do:

                        QThread* thread = new QThread;
                        ...
                        thread->start();
                        

                        it will use the default implementation of QThread which will run an event loop.

                        In between creating the thread and starting it you do:

                        QObject::connect(thread, SIGNAL(started()), &a, SLOT(processList()), Qt::AutoConnection);
                        

                        which means that when the thread starts it will immediately start processing the first slot processList. Only after this slot finishes will the thread be able to process the next event from its event loop. However, you have a

                        while(running)
                        {
                            ...
                        }
                        

                        in you processList slot. So, I guess that your slot never finishes and QThread will never pick up any other events.

                        One trick you can try is to put a repeating QTimer in your thread which has a timeout of 0ms and is connected to processList (assuming you remove the while(running)...). This will call processList over and over again when there is nothing else to do (kind of an infinite loop like you have now). But, between calls to processList other events can be handled, i.e. executing makeUpdate in your specific case.

                        1 Reply Last reply
                        2
                        • D Offline
                          D Offline
                          dan13l
                          wrote on last edited by
                          #16

                          Thank you again for your engagement regarding my questions! πŸ™‚

                          @kshegunov ah, okay, so thank you for that explanation. I think now the example code is improved, because I need no specification for the Connection Type. (see below)

                          @KroMignon also thanks to you for the deeper insight to to Connection Types. Yes, it helps me, also regarding some code efficiency considerations due to the creation of shallow copies.

                          @SimonSchroeder Thanks a lot! First I did not think about further events, but in the end this is what I want to achieve. Therefore your 'trick' addresses directly my implementation problem and solves it. Fantastic!
                          I hope I understood your idea correctly? What I did is the following:

                          main.cxx

                          #include <QApplication>
                          #include <QThread>
                          #include <iostream>
                          #include "A.h"
                          #include "B.h"
                          int main(int argc , char *argv[]){
                            QApplication app(argc, argv);
                            //doing something for gui and user ...
                            // ...
                            std::cout<<"Hello Qt Forum."<<std::endl;
                            // ...
                            A a(1);
                            B b(2);
                            //creating thread 
                            QThread* thread = new QThread;
                            a.moveToThread(thread); // change thread affinity
                            bool check1, check2;
                            check1 = QObject::connect(thread, &QThread::started, &a, &A::timing);
                            check2 = QObject::connect(&b, &B::updater , &a, &A::makeUpdate);
                            std::cout<<"successful connection?: check1= "<<check1<<" and check2= "<<check2<<std::endl;
                          
                            //starting thread
                            thread->start();
                            
                            //now: give an update from B to A:
                            emit b.updater(42); // --> now it`s working
                            for(int i = 0; i < 123; i++) std::cout<<"here in main with i = "<<i<<std::endl;
                            emit b.updater(1001);// --> and again...
                            return app.exec();
                          }
                          

                          A.cxx (t is my timer defined in A.h with this=A as parent. Otherwise it will give me 'QObject::killTimer: Timers cannot be stopped from another thread' . I think it is due to different thread affinity.

                          #include "A.h"
                          #include <iostream>
                          //
                          A::A (int id){
                            name = id;
                            t->callOnTimeout(this, &A::processList);
                          }
                          
                          void A::timing(){
                            std::cout<<thread()<<std::endl;
                              t->start();
                            
                          }
                          
                          void A::processList(){
                            std::cout<<"in processList"<<std::endl;
                            if( !buffer.isEmpty() ){
                              list.append(buffer);
                              std::cout<<"Now list size is: "<<list.size()<<std::endl;
                              buffer.clear();
                              std::cout<<"after cleaning buffer, buffer size is:"<<buffer.size()<<std::endl;
                            }
                            timing();
                          }
                          
                          void A::makeUpdate( int update ){
                            std::cout<<"Updating..."<<std::endl;
                            std::cout<<thread()<<std::endl;
                            buffer.append(update);
                          }
                          
                          

                          as my result, my console output gives me the following:

                          Hello Qt Forum.
                          successful connection?: check1= 1 and check2= 1
                          here in main with i = 0
                          here in main with i = 1
                          here in main with i = 2
                          here in main with i = 3
                          ...
                          0x16571e8
                          ...
                          Updating...
                          0x16571e8
                          ...
                          here in main with i = 40
                          here in main with i = 41
                          ...
                          in processList
                          Now list size is: 1
                          after cleaning buffer, buffer size is:0
                          0x16571e8
                          ...
                          here in main with i = 100
                          here in main with i = 101
                          ...
                          here in main with i = 122
                          in processList
                          0x16571e8
                          Updating...
                          0x16571e8
                          in processList
                          Now list size is: 2
                          after cleaning buffer, buffer size is:0
                          0x16571e8
                          in processList
                          ...
                          
                          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