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. Emitting Signal from Slot problem in multithreaded environment
Forum Updated to NodeBB v4.3 + New Features

Emitting Signal from Slot problem in multithreaded environment

Scheduled Pinned Locked Moved Solved General and Desktop
13 Posts 4 Posters 4.8k Views 1 Watching
  • 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.
  • F Offline
    F Offline
    Floofy.KH
    wrote on last edited by
    #1

    Hi

    I've encountered a problem trying to get classes to communicate between threads. I couldn't find any information about this particular scenario so not sure if it's expected behaviour or not.

    I have ObjectA which is created on thread A.
    And ObjectB is created on thread B.

    A signal from ObjectA is connected to a slot on ObjectB, as a QueuedConnection.
    And vice verse, a signal from ObejctB is connected to a slot on ObjectA as a QuededConnection.
    I've tried various combinations of connection types (however they need to be either Queued or BlockingQueued), none solve the problem.

    In the slot for ObjectB, I emit the ObjectB's signal (connected back to ObjectA).

    Emitting ObjectA's signal, I expect both slots to be called. Only one does, ObjectB's. The connection from ObjectB to ObjectA succeeds, however the slot is never called.

    Stepping through the Qt code when emitting seems to suggest a problem occurs in qcoreapplication.cpp in QCoreApplication::postEvent at the end of the function, the following lines:

    QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
        if (dispatcher)
            dispatcher->wakeUp();
    

    the return from loadAcquire always returns NULL for the second signal.

    A minimal example I managed to replicate this behaviour in is below.

    #ifndef MAINOBJECT
    #define MAINOBJECT
    
    #include <iostream>
    #include <QObject>
    
    class MainObject : public QObject
    {
        Q_OBJECT
    
    public:
        MainObject(){}
    
    public slots:
        void mainObjectSlot()
        {
            std::cout << "Main slot\nEmitting main signal...\n";
            emit mainObjectSignal();
        }
    
    signals:
        void mainObjectSignal();
    };
    
    #endif // MAINOBJECT
    
    #ifndef WORKERTHREAD
    #define WORKERTHREAD
    
    #include "mainobject.h"
    
    #include <QThread>
    
    class WorkerObject : public QObject
    {
        Q_OBJECT
    
    public:
        void emitSig()
        {
            std::cout << "Emitting worker signal...\n";
            emit workerObjectSignal();
        }
    
    public slots:
        void workerObjectSlot()
        {
            std::cout << "Worker slot\n";
        }
    
    signals:
        void workerObjectSignal();
    };
    
    class WorkerThread : public QThread
    {
        Q_OBJECT
    
    private:
        MainObject *m_mainObj;
    
    public:
        WorkerThread(MainObject *obj) : m_mainObj(obj) {}
    
    public slots:
        void run() Q_DECL_OVERRIDE
        {
            WorkerObject *obj = new WorkerObject;
    
            if(!connect(obj, SIGNAL(workerObjectSignal()),
                    m_mainObj, SLOT(mainObjectSlot()),
                    Qt::QueuedConnection))
            {
                std::cout << "Failed to connect worker signal to main slot.\n";
            }
            if(!connect(m_mainObj, SIGNAL(mainObjectSignal()),
                    obj, SLOT(workerObjectSlot()),
                    Qt::QueuedConnection))
            {
                std::cout << "Failed to connect main signal to worker slot.\n";
            }
    
            obj->emitSig();
        }
    };
    #endif // WORKERTHREAD
    
    #include <QCoreApplication>
    
    #include "workerthread.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        WorkerThread thread(new MainObject);
        thread.start();
    
        return a.exec();
    }
    

    I get the following output when I run this.
    0_1472566972085_Qt.PNG

    I am using Qt version 5.4.1 on Windows.

    Any help would be appreciated.

    1 Reply Last reply
    0
    • kshegunovK Offline
      kshegunovK Offline
      kshegunov
      Moderators
      wrote on last edited by kshegunov
      #2
      class WorkerThread : public QThread
      {
          Q_OBJECT
      
          // ...
      protected:
          void run() Q_DECL_OVERRIDE //< run() isn't a slot and should be left protected.
          {
              // ...
              exec() //< You can't have queued connections without an event loop
          }
      };
      

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply
      1
      • F Offline
        F Offline
        Floofy.KH
        wrote on last edited by
        #3

        Thanks, that definitely solved the problem with the example.

        Is there a way to have queued connections work within another thread when the thread is not within a QThread, ie. when exec() is not available? In my actual code the thread is created using AfxBeginThread().

        T 1 Reply Last reply
        0
        • VRoninV Offline
          VRoninV Offline
          VRonin
          wrote on last edited by
          #4

          you will need a Qt event loop to process Qt signals with queued connections. given you have to mix MFC and Qt I'd use a signal system that is independent from both while still being thread safe to make them interact. The first thing that comes in mind is, of course, Boost.Signals2 http://www.boost.org/doc/libs/1_61_0/doc/html/signals2.html

          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
          ~Napoleon Bonaparte

          On a crusade to banish setIndexWidget() from the holy land of Qt

          1 Reply Last reply
          1
          • F Floofy.KH

            Thanks, that definitely solved the problem with the example.

            Is there a way to have queued connections work within another thread when the thread is not within a QThread, ie. when exec() is not available? In my actual code the thread is created using AfxBeginThread().

            T Offline
            T Offline
            t3685
            wrote on last edited by
            #5

            @Floofy.KH

            IIRC QThread::exec() starts a event loop (QEventLoop). Signals coming from a different thread a placed as events in the event loop of the receiving thread. I do not think it's possible to have Qt event handling without this event loop. Maybe you can use your own QEventLoop object AfxBeginThread thread?

            F 1 Reply Last reply
            1
            • T t3685

              @Floofy.KH

              IIRC QThread::exec() starts a event loop (QEventLoop). Signals coming from a different thread a placed as events in the event loop of the receiving thread. I do not think it's possible to have Qt event handling without this event loop. Maybe you can use your own QEventLoop object AfxBeginThread thread?

              F Offline
              F Offline
              Floofy.KH
              wrote on last edited by Floofy.KH
              #6

              @t3685

              Thanks. That seemed to solve the problem. Creating a QEventLoop object inside the thread and calling exec() on it allowed all events to be processed correctly.

              Thanks for all the help.

              edit: @VRonin Using an independent signalling system such as Boost would be a good idea too, which I will consider. Thanks.

              kshegunovK 1 Reply Last reply
              0
              • F Floofy.KH

                @t3685

                Thanks. That seemed to solve the problem. Creating a QEventLoop object inside the thread and calling exec() on it allowed all events to be processed correctly.

                Thanks for all the help.

                edit: @VRonin Using an independent signalling system such as Boost would be a good idea too, which I will consider. Thanks.

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

                @Floofy.KH
                @t3685 beat me to it, this is what I would have suggested. QThread::exec calls QEventLoop::exec anyway (same with QCoreApplication::exec), so you can substitute it with a call to the local event loop most of the time. I would, however, also suggest you try and migrate the WinAPI portions of your code to Qt (i.e. not creating the threads with AfxBeginThread), which incidentally will also make your code portable.

                Kind regards.

                Read and abide by the Qt Code of Conduct

                VRoninV 1 Reply Last reply
                0
                • kshegunovK kshegunov

                  @Floofy.KH
                  @t3685 beat me to it, this is what I would have suggested. QThread::exec calls QEventLoop::exec anyway (same with QCoreApplication::exec), so you can substitute it with a call to the local event loop most of the time. I would, however, also suggest you try and migrate the WinAPI portions of your code to Qt (i.e. not creating the threads with AfxBeginThread), which incidentally will also make your code portable.

                  Kind regards.

                  VRoninV Offline
                  VRoninV Offline
                  VRonin
                  wrote on last edited by
                  #8

                  @kshegunov @t3685 Won't calling QEventLoop::exec from the MFC thread block it until exec returns?

                  "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                  ~Napoleon Bonaparte

                  On a crusade to banish setIndexWidget() from the holy land of Qt

                  kshegunovK 1 Reply Last reply
                  0
                  • VRoninV VRonin

                    @kshegunov @t3685 Won't calling QEventLoop::exec from the MFC thread block it until exec returns?

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

                    @VRonin
                    It will, but I assume that's one of the reasons he's starting the thread to begin with.

                    Read and abide by the Qt Code of Conduct

                    VRoninV 1 Reply Last reply
                    0
                    • kshegunovK kshegunov

                      @VRonin
                      It will, but I assume that's one of the reasons he's starting the thread to begin with.

                      VRoninV Offline
                      VRoninV Offline
                      VRonin
                      wrote on last edited by
                      #10

                      @kshegunov Let me rephrase: aren't we just flipping the pancake here? if you don't call QEventLoop::exec() you won't be able to process Qt signals, but if you call it then MFC will not be able to process its events any more.

                      "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                      ~Napoleon Bonaparte

                      On a crusade to banish setIndexWidget() from the holy land of Qt

                      kshegunovK 1 Reply Last reply
                      0
                      • VRoninV VRonin

                        @kshegunov Let me rephrase: aren't we just flipping the pancake here? if you don't call QEventLoop::exec() you won't be able to process Qt signals, but if you call it then MFC will not be able to process its events any more.

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

                        @VRonin said in Emitting Signal from Slot problem in multithreaded environment:

                        Let me rephrase: aren't we just flipping the pancake here?

                        I honestly don't know, we may be. The thing is I have only partial information on what are the requirements and reasoning behind them, so I can at most guess about the initial intent.

                        If you don't call QEventLoop::exec() you won't be able to process Qt signals, but if you call it then MFC will not be able to process its events any more.

                        I have forgotten most of the MFC-related API, but what events does it needs processing (in a thread)? I understand that for the application there's integration with the system event loop (both for Qt and for MFC), but I believe there's no system event loop for threads. On a related note, the event loop is needed only for queued connections, so he can use direct connections (with proper locking) without it, thus the boost's signal support sadly doesn't bring anything new here.

                        Read and abide by the Qt Code of Conduct

                        VRoninV 1 Reply Last reply
                        0
                        • kshegunovK kshegunov

                          @VRonin said in Emitting Signal from Slot problem in multithreaded environment:

                          Let me rephrase: aren't we just flipping the pancake here?

                          I honestly don't know, we may be. The thing is I have only partial information on what are the requirements and reasoning behind them, so I can at most guess about the initial intent.

                          If you don't call QEventLoop::exec() you won't be able to process Qt signals, but if you call it then MFC will not be able to process its events any more.

                          I have forgotten most of the MFC-related API, but what events does it needs processing (in a thread)? I understand that for the application there's integration with the system event loop (both for Qt and for MFC), but I believe there's no system event loop for threads. On a related note, the event loop is needed only for queued connections, so he can use direct connections (with proper locking) without it, thus the boost's signal support sadly doesn't bring anything new here.

                          VRoninV Offline
                          VRoninV Offline
                          VRonin
                          wrote on last edited by
                          #12

                          @kshegunov True, we don't have enough info. but MFC allows to run a new GUI thread: https://msdn.microsoft.com/en-us/library/b807sta6.aspx and that would not work if we call QEventLoop::exec().

                          so he can use direct connections (with proper locking)

                          I thought Boost.Signal2 was exactly that. mutex-ed direct connection signals

                          "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                          ~Napoleon Bonaparte

                          On a crusade to banish setIndexWidget() from the holy land of Qt

                          kshegunovK 1 Reply Last reply
                          1
                          • VRoninV VRonin

                            @kshegunov True, we don't have enough info. but MFC allows to run a new GUI thread: https://msdn.microsoft.com/en-us/library/b807sta6.aspx and that would not work if we call QEventLoop::exec().

                            so he can use direct connections (with proper locking)

                            I thought Boost.Signal2 was exactly that. mutex-ed direct connection signals

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

                            @VRonin said in Emitting Signal from Slot problem in multithreaded environment:

                            and that would not work if we call QEventLoop::exec()

                            You won't get any argument from me on that one.

                            I thought Boost.Signal2 was exactly that. mutex-ed direct connection signals

                            I didn't know it existed, but yes, I suppose that's a possible solution. The only problem I envision with that approach, taking in mind that new piece of information, is that mixing Qt's signal slot system and an external one is (sometimes) a big pain in the ass ...

                            Read and abide by the Qt Code of Conduct

                            1 Reply Last reply
                            1

                            • Login

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