Problem with signal-slot connection across threads [SOLVED]
-
wrote on 7 Mar 2014, 17:26 last edited by
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.
-
wrote on 7 Mar 2014, 20:32 last edited by
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?
-
Hi,
is subWorkerObj a class member ? If not it will probably be destroyed before it does anything.
-
wrote on 8 Mar 2014, 16:48 last edited by
I'll get some code here asap.
@SGaist
subWorkerObj is indeed a class member. -
wrote on 8 Mar 2014, 17:03 last edited by
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?
-
I'm not sure I am following you correctly, can you show the related code ?
-
wrote on 9 Mar 2014, 16:08 last edited by
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_OBJECTpublic:
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_OBJECTpublic:
SubWorkerObj();signals:
void signalFromSubWorker();public slots:
void beginWork();
};
#endif // SUBWORKEROBJ_H
@SubWorkerObj.cpp
@
#include "subworkerobj.h"SubWorkerObj::SubWorkerObj()
{
}void SubWorkerObj::beginWork()
{
emit signalFromSubWorker();
}
@ -
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
-
wrote on 10 Mar 2014, 09:09 last edited by
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?
-
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.
-
wrote on 10 Mar 2014, 11:58 last edited by
Ah thank you JKSH for clearing some of this out.
1/11