Problem with signal-slot connection across threads [SOLVED]



  • I know this should pretty much work "out of the box" if you pay attention to some details, which I tried to - but I must have missed something.

    I have a worker thread which starts another worker thread let's call it subworker. The worker and the subworker need to communicate via signals and slots. I make all the connections from the worker thread, and all the connect() statements return true when running (also, there's no warnings reported in the debug output).

    The relevant worker thread code looks like this:
    @
    //! The subworker will live in its own thread
    subWorkerThread = new QThread(this);
    subWorkerObj.moveToThread(subWorkerThread);

    //! Let it live!
    subWorkerThread->start();

    bool ok = connect(this, SIGNAL(work(QString)), &subWorkerObj, SLOT(beginWork(QString)));
    ok = connect(&subWorkerObj, SIGNAL(signalFromSubWorker()), this, SLOT(handleSignalFromSubWorker()));
    @

    Then inside the subWorkerObj::beginWork:
    @
    emit signalFromSubWorker();
    @

    My debugging shows me that the beginWork() slot is correctly hit, but after the signal from SubWorker is emitted, I'm not hitting the breakpoint inside the handleSignalFromSubWorker() slot.

    Both classes subclass QObject (or a QObject subclass), both have the Q_OBJECT macro, and I made the slot in the worker public just in case.

    I'd appreciate any help, even about how to better debug it.



  • difficult to say, from the code I can see there is no problem, can you post all the code from worker and subworker (h and c files), I think the issue will become clearer then!

    Alternatively, you could go back a step and forget the thread for now, just create two objects (worker and subworker) connected a signal / slot one in each direction (as you have done) and try that.

    Make sure you declare the signals and the slots in the respective header files.

    Can you send multiple work() signals to the sub worker? - is it getting stuck?


  • Lifetime Qt Champion

    Hi,

    is subWorkerObj a class member ? If not it will probably be destroyed before it does anything.



  • I'll get some code here asap.

    @SGaist
    subWorkerObj is indeed a class member.



  • I might have figured this out. My main application thread is starting my first worker thread but it has it's own loop in a run() method, without calling exec(). Would that mean that for this worker thread there's no event loop to process incoming signals?


  • Lifetime Qt Champion

    I'm not sure I am following you correctly, can you show the related code ?



  • I've created a project on the side with the same setup to reproduce the problem.

    It's based off the basic "new Widget Application". In the MainWindow I have a WorkerObjHost running in its own thread, it is creating the WorkerObj running in its own thread, which in turn creates the SubWorker in its own (third thread). The issue is with the connection between Worker and SubWorker.

    Edit: the code here is deemed too complex and lacking event loops to actually process the slots, any late-comers to the thread, please don't spend too much time figuring it out.
    Code:
    MainWindow.h
    @
    // Created this pushButton to start things off
    public slots:
    void on_pushButton_clicked();

    private:
    Ui::MainWindow *ui;
    WorkerObjHost *workerObjHost;
    @

    In MainWindow.cpp
    @
    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    workerObjHost = new WorkerObjHost(this);
    }

    void MainWindow::on_pushButton_clicked()
    {
    workerObjHost->start();
    }
    @

    WorkerObjHost.h
    @
    #ifndef WORKEROBJHOST_H
    #define WORKEROBJHOST_H

    #include <QThread>
    class WorkerObjHost : public QThread
    {
    public:
    WorkerObjHost(QObject *parent = NULL);
    virtual void run();
    };

    #endif // WORKEROBJHOST_H
    @

    WorkerObjHost.cpp
    @
    #include "workerobjhost.h"
    #include "workerobj.h"

    WorkerObjHost::WorkerObjHost(QObject *parent)
    : QThread(parent)
    {
    }

    void WorkerObjHost::run()
    {
    // not parented here, but seems it doesn't affect outcome
    WorkerObj *worker = new WorkerObj;
    worker->start();

    while (true) {
    // Do some work
    }
    }
    @

    WorkerObj.h
    @
    #ifndef WORKEROBJ_H
    #define WORKEROBJ_H

    #include <QThread>
    #include "subworkerobj.h"

    class WorkerObj : public QThread
    {
    Q_OBJECT

    public:
    WorkerObj(QObject *parent = NULL);

    signals:
    void work();

    public slots:
    void handleSignalFromSubWorker();

    protected:
    virtual void run();
    };

    #endif // WORKEROBJ_H
    @

    WorkerObj.cpp
    @
    #include "workerobj.h"
    #include "subworkerobj.h"

    WorkerObj::WorkerObj(QObject *parent)
    : QThread(parent)
    {
    }

    void WorkerObj::run()
    {
    SubWorkerObj subWorkerObj;
    QThread *subWorkerThread = new QThread;
    subWorkerObj.moveToThread(subWorkerThread);
    subWorkerThread->start();

    // Both connections return true
    bool ok = connect(this, SIGNAL(work()), &subWorkerObj, SLOT(beginWork()));
    ok = connect(&subWorkerObj, SIGNAL(signalFromSubWorker()), this, SLOT(handleSignalFromSubWorker()));

    emit work();

    while (true)
    {
    // do other things
    }
    }

    void WorkerObj::handleSignalFromSubWorker()
    {
    // This doesn't get called
    }
    @

    SubWorkerObj.h
    @
    #ifndef SUBWORKEROBJ_H
    #define SUBWORKEROBJ_H

    #include <QObject>

    class SubWorkerObj : public QObject
    {
    Q_OBJECT

    public:
    SubWorkerObj();

    signals:
    void signalFromSubWorker();

    public slots:
    void beginWork();
    };
    #endif // SUBWORKEROBJ_H
    @

    SubWorkerObj.cpp
    @
    #include "subworkerobj.h"

    SubWorkerObj::SubWorkerObj()
    {
    }

    void SubWorkerObj::beginWork()
    {
    emit signalFromSubWorker();
    }
    @


  • Lifetime Qt Champion

    You're making things overly complicated. You have three levels of thread and none of them has an active event loop. So signals and slots between them will not work as you think.

    Have a look at the latest "QThread documentation":http://qt-project.org/doc/qt-5/qthread.html you'll see there how to implement the worker object paradigm



  • Thanks SGaist. You have to guess the rest of the code from what I posted but it's built up that way for a reason (and of course, the original design didn't consider using signals and slots at that point). But I think I can make some changes and get an event loop running.

    There's something I don't really understand. Is an event loop always necessary on the thread that is supposed to execute the connected slot? It seems that emitting the signal works even if I don't have an event loop, and if the connected slot is on the same thread it executes directly. So basically once I want cross thread signals and slots, I need an event loop on the thread with the slots?


  • Moderators

    Hi,

    [quote]Is an event loop always necessary on the thread that is supposed to execute the connected slot?[/quote]Yes.

    [quote]It seems that emitting the signal works even if I don’t have an event loop,[/quote]You don't need an event loop to emit signals.

    [quote]and if the connected slot is on the same thread it executes directly.[/quote]Yes, a "DirectConnection":http://qt-project.org/doc/qt-5/qt.html#ConnectionType-enum bypasses the event loop to make a direct function call.

    [quote] So basically once I want cross thread signals and slots, I need an event loop on the thread with the slots?[/quote]Correct.



  • Ah thank you JKSH for clearing some of this out.


Log in to reply
 

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