Send signal to from MainWindow.cpp to QThread::run()



  • Hey,

    I'm trying to communicate between my QMainWindow (ZentralSG) and a QThread::run() function loop (SpeakerThread). I tried the following, but the program crashes.

    ZentralSG.h

    #ifndef ZENTRALSG_H
    #define ZENTRALSG_H
    
    #include <speakerthread.h>
    
    namespace Ui {
    class ZentralSG;
    }
    
    class ZentralSG : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit ZentralSG(QWidget *parent = 0);
        ~ZentralSG();
    
        bool get_EPHMode();
    
    private slots:
        void on_pushButton_clicked();
    
    private:
        Ui::ZentralSG *ui;
    
        SpeakerThread *m_speakerThread;
    
        bool EPHMode;
    
        void Init();
    };
    
    #endif // ZENTRALSG_H
    

    ZentralSG.cpp

    ZentralSG::ZentralSG(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::ZentralSG)
    {
        ui->setupUi(this);
    
        m_speakerThread = new SpeakerThread();
        EPHMode = false;
    
        this->Init();
    }
    
    ZentralSG::~ZentralSG()
    {
        delete ui;
    }
    
    ZentralSG::Init()
    {
        m_speakerThread->Init();
        m_speakerThread->start();
    
    void ZentralSG::on_pushButton_clicked()
    {
        EPHMode = true;
    }
    
    void ZentralSG::get_EPHMode()
    {
        return EPHMode;
    }
    

    SpeakerThread.h

    #ifndef SPEAKERTHREAD_H
    #define SPEAKERTHREAD_H
    
    class ZentralSG;
    
    class SpeakerThread : public QThread
    {
        Q_OBJECT
    public:
        explicit SpeakerThread(QObject *parent = 0);
        ~SpeakerThread();
    
        void Init();
        void shutdown();
        void run();
    
    private:
        ZentralSG *m_zsg;
    
        bool speak_running;
    };
    
    #endif // SPEAKERTHREAD_H
    

    SpeakerThread.cpp

    #include "zentralsg.h"
    
    SpeakerThread::SpeakerThread(QObject *parent) :
        QThread(parent)
    {
    
    }
    
    SpeakerThread::~SpeakerThread()
    {
    
    }
    
    void SpeakerThread::Init()
    {
        speak_running = true;
    }
    
    void SpeakerThread::run()
    {
        while(speak_running)
        {
            if (m_zsg->get_EPHMode() == true)
            {
                 // Do something
            }
        }
    }
    
    void SpeakerThread::shutdown()
    {
        speak_running = false;
    }
    
    

    So I want to know when EPHMode is set true and then do something in my SpeakerThread.cpp. Can anyone help me with this?

    Thank you!



  • @Beatsteak I think are //do something related.... (ex. exist your speaker file or something similar that can result similar to array, vector or matrix empty but instead should be !empty ????)
    In my experience if you are in Qthread if not find some array value the program cresh whitout advice or debug info .... try to use debug mode with valgrind to see the memory exact point ....

    regards
    Giorgio



  • Hi Giorgio,

    the program also crashes when I do nothing but the m_zsg->get_EPHMode() in my SpeakerThread.



  • Even if you fix the crash this won't work as you don't have an event loop on the thread. This is partially due to the QThread documentation being 💩.
    See https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ for a better method



  • I would approach this a little differently. I don't know if something along these lines is possible in your case but I don't see why it wouldn't work.

    First the run member of the thread should do only one task then exit. If, for example, you need it to say 'Hello' then it should exit once this is done. The event loop for the thread doesn't work if you stay inside the run function and wait for commands.

    The global scope variables work but this will come back to haunt you later. It is better to avoid this if possible. You can use member functions of your QThread subclass to replace this.

    So, if your QThread subclass has a public slot called Say_Hello(void) this would trigger the run member which exits when it is done. You connect the signal from your push button to this slot and you should be good. You can watch the finished() signal from QThread if you need to monitor when it is done.

    If you need it to say different things then you could have different slots or use a member function to set the text of what is said with a single slot.

    There is another way to use QThread if you don't want to subclass it. If it is one task that takes a long time to complete then maybe that approach works better. The method was posted on this forum a number of years ago so if you search for it you should find it.



  • @Rondog said in Send signal to from MainWindow.cpp to QThread::run():

    First the run member of the thread should do only one task then exit. If, for example, you need it to say 'Hello' then it should exit once this is done. The event loop for the thread doesn't work if you stay inside the run function and wait for commands.

    This is not 100% true but this is not the point here. I think the main problems are:

    • not declaring run() as protected and virtual
    • EPHMode being a race condition
    • speak_running being a race condition
    • m_zsg used as dangling pointer as it's not set anywhere (this is the thing causing the crash btw)
    • no event loop in the thread prevents correct functioning of signals and slots

    Since this is the wrong what of using QThread anyway i's just better to rewrite it using the blueprint in the link above


  • Qt Champions 2016

    @VRonin said in Send signal to from MainWindow.cpp to QThread::run():

    • not declaring run() as protected and virtual

    This is a problem only from our point of view as programmers. The compiler (and linker) don't care for either one if the signature matches (overriding doesn't enforce access scope).

    • no event loop in the thread prevents correct functioning of signals and slots

    Yes, but only queued slots. ;)

    @Beatsteak said in Send signal to from MainWindow.cpp to QThread::run():

    I'm trying to communicate between my QMainWindow (ZentralSG) and a QThread::run() function loop (SpeakerThread). I tried the following, but the program crashes.

    If you're not well familiarized with threading I advise to follow @VRonin's suggestion - read on the worker object, otherwise you need to provide manual synchronization between the threads to avoid race conditions.

    void SpeakerThread::Init()
    {
        speak_running = true;
    }
    
    void SpeakerThread::run()
    {
        while(speak_running)
        {
            // ...
        }
    }
    
    void SpeakerThread::shutdown()
    {
        speak_running = false;
    }
    

    To use the flag like this it should be at least made volatile. If you don't know what volatile is or how it's used, I reiterate my advice - use @VRonin's suggested solution.



  • @VRonin said in Send signal to from MainWindow.cpp to QThread::run():

    @Rondog said in Send signal to from MainWindow.cpp to QThread::run():

    First the run member of the thread should do only one task then exit. If, for example, you need it to say 'Hello' then it should exit once this is done. The event loop for the thread doesn't work if you stay inside the run function and wait for commands.

    This is not 100% true but this is not the point here. ...

    @VRonin you are right and I am 100% wrong. I ran a simple test with a signal/slot inside a subclassed QThread::run() member that contained a loop which emitted a signal on every iteration; it worked perfectly. I have avoided doing this for some time now as I always believed the message queue would be blocked by something like this. Live and learn I guess ...


  • Qt Champions 2016

    @Rondog said in Send signal to from MainWindow.cpp to QThread::run():

    I have avoided doing this for some time now as I always believed the message queue would be blocked by something like this. Live and learn I guess ...

    The problem is not emitting signals. Signals are impervious to event loop blocking, the problem is the receiving slot. Suppose you have an object that's moved to a thread whose event loop is blocked. This object then can only have its slots invoked directly (i.e. either by signals emitted from objects in the same thread, or by explicitly specifying the connection type on connect()). If you try to have that object's slot invoked from a signal emitted by object living in another thread, and you don't change the connection type (or use Qt::QueuedConnection) then your slot will be invoked only after the control has been returned to the event loop (in the case of reimplementing run() probably never).



  • @kshegunov very interesting (and somewhat confusing). This is the test I wrote to see if signals and slots worked inside a thread that runs in a loop (they do). I have some follow up questions below:

    MyThread::MyThread(QObject *parent)
    :QThread(parent)
    {
        connect(this,SIGNAL(My_Thread_Signal(const int)),this,SLOT(My_Thread_Slot(const int)));
    }
    
    MyThread::~MyThread(void)
    {
        disconnect(this,SIGNAL(My_Thread_Signal(const int)),0,0);
    }
    
    void MyThread::run(void)
    {
    	
        for(int cntr = 0;cntr < 6;++cntr)
        {
            qWarning("sending signal ...");
            emit My_Thread_Signal(cntr);
            this->msleep(1000);
        }
    	
        qWarning("done thread");
    }
    
    void MyThread::My_Thread_Slot(const int ival)
    {
        qWarning(QString("signal received: %1").arg(ival).toLatin1());
    }
    

    So, after thinking about this for a while the constructor and destructor live in the main GUI thread of the program so the connect and disconnect statements are done in the main thread. Everything that is done inside 'run()' is really the only thing that exists within the new thread.

    I am not sure what to say about the slot though. Is this what 'QueuedConnection' is designed to address? If I understand properly then without this it would be executed inside the thread that invoked the signal (direct connection) and with it would run inside the main GUI thread of the program?

    What I didn't try was to create a new object of something inside the 'run()' member and see if this can emit or respond to signals. I would expect it wouldn't work because this is exactly how it would work in the main GUI thread (if you have something that blocks in the main thread your program would appear to freeze as no events are processed).

    Does a QueuedConnection cause the sender of the emitted signal to be blocked until it is completed? Would this work more like a Win32 PostMessage command (as opposed to a SendMessage command)?


  • Qt Champions 2016

    @Rondog said in Send signal to from MainWindow.cpp to QThread::run():

    @kshegunov very interesting (and somewhat confusing).

    :)

    So, after thinking about this for a while the constructor and destructor live in the main GUI thread of the program so the connect and disconnect statements are done in the main thread.

    It doesn't matter where you make the actual connect. QObject::connect() is thread-safe. More explanation follows below.

    Everything that is done inside 'run()' is really the only thing that exists within the new thread.

    This is exactly correct.

    Is this what 'QueuedConnection' is designed to address?

    In a sense, yes.

    If I understand properly then without this it would be executed inside the thread that invoked the signal (direct connection) and with it would run inside the main GUI thread of the program?

    Correct.

    What I didn't try was to create a new object of something inside the 'run()' member and see if this can emit or respond to signals. I would expect it wouldn't work because this is exactly how it would work in the main GUI thread (if you have something that blocks in the main thread your program would appear to freeze as no events are processed).

    If you create an object in run() it can emit signals fine. It can't have its slots invoked when the emitter is in another thread, however. This is because you don't have a running event loop in the receiving thread.

    Does a QueuedConnection cause the sender of the emitted signal to be blocked until it is completed?

    Yes for a minute time. The whole story is like this:

    1. When you put something as a signal the moc creates a regular function body for you.
    2. When you do emit mySignal() you just call the generated function (emit is just syntax sugar, it expands to nothing).
    3. Inside this generated function (or rather inside some functions internal to Qt) a list of slots and receiver objects for this signals is acquired.
    4. Depending on the connection type you used when doing QObject::connect there are few possibilities:
      1. You used Qt::DirectConnection - The receiver's slot is called in the thread the emit was done in. It's equivalent to directly calling the receiver's slot from the point where the emit was done.
      2. You used Qt::QueuedConnection - An event (with type QEvent::MetaCall) object is created and it is posted in the receiver object's thread's event loop for later processing. After posting, the execution of the current thread (the one emitting the signal) continues normally. Implication is, that the slot will be called later from the event processing routines, thus it will be called in the context of the receiver object's thread.
      3. You used Qt::AutoConnection - The default.
        • If the receiver object lives in the same thread as the emit was done, then the slot is called directly (like in `Qt::DirectConnection).
        • If the receiver object lives in a thread different than the one of the signal emission, then the slot call is queued (like in Qt::QueuedConnection).
      4. You used Qt::BlockingQueuedConnection, exactly the same as Qt::QueuedConnection, but after posting the event to the receiver thread's event loop you wait for the receiver thread to finish processing it, and only then execution continues.

    Would this work more like a Win32 PostMessage command (as opposed to a SendMessage command)?

    No, you have QCoreApplication::postEvent and QCoreApplication::sendEvent that work like this.

    I hope that clears it up. If you need examples to illustrate, don't hesitate to ask.
    Kind regards.



  • @kshegunov Thanks for the detailed explanation. I definately have a better understanding of all this and now I have some programs I want to modify ...

    Sorry if I hijacked this thread.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.