Reputation: 8527
According to the documentation for Qt::ConnectionType in Qt5, using Qt::DirectConnection
means that the slot for a given signal is called in the same thread as the signal itself, even if the object the slot belongs to lies in a different thread.
In my application, I am creating a server, and when a new connection is received, I create a new thread, a new QWebSocket
object in it, and connect certain QWebSocket
signals to slots defined in the class of the server (the same class where the connection is received and the thread is created).
However, though the thread is created successfully, the slot is being called in the main thread.
Here is a simpler example that simulates what I am doing, an MCVE:
Base.h file:
#ifndef BASE_H
#define BASE_H
#include<QThread>
#include<thread>
#include<QObject>
#include "emitcaller.h"
#include <QDebug>
class Base : public QObject
{
Q_OBJECT
public:
EmitCaller *emitCaller;
void create_thread();
void make_emit();
public slots:
void do_something();
};
#endif // BASE_H
This represents the server class. create_thread()
is like the function when a new connection from a client is to be done. do_something()
is the slot that needs to be executed when the QWebSocket
receives a signal.
Base.cpp file:
#include "base.h"
#include "emitcaller.h"
#include<QEventLoop>
#include <mutex>
#include <condition_variable>
void Base::create_thread()
{
std::mutex mutex;
std::condition_variable cv;
std::thread t = std::thread([&](){
EmitCaller *ec = new EmitCaller;
this->emitCaller = ec;
qDebug() << "thread created, now in thread " << QThread::currentThread();
QObject::connect(ec,SIGNAL(my_signal()),this,SLOT(do_something()),Qt::DirectConnection);
cv.notify_all();
QEventLoop loop;
loop.exec();
});
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock); //wait till connect() completes, so that signal sent is received after that
t.detach();
}
void Base::do_something()
{
qDebug() << "doing something in thread " << QThread::currentThread();
}
void Base::make_emit()
{
qDebug() << "called make_emit in thread " << QThread::currentThread();
emitCaller->do_emit();
}
Next, EmitCaller.h file:
#ifndef EMITCALLER_H
#define EMITCALLER_H
#include <QObject>
#include <QDebug>
class EmitCaller : public QObject
{
Q_OBJECT
public:
void do_emit();
signals:
void my_signal();
};
#endif // EMITCALLER_H
This is to simulate the QWebSocket
. The my_signal()
signal is the one that the QWebSocket
in the program receives to call the do_something()
slot. make_emit()
is an extra function, just to ask the signal to be emitted, created only for the sake of this simplified example.
EmitCaller.cpp file:
#include "emitcaller.h"
void EmitCaller::do_emit()
{
emit my_signal();
}
main.cpp file:
#include <QApplication>
#include "base.h"
#include <QDebug>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Base b;
qDebug() << "main in thread " << QThread::currentThread();
b.create_thread();
b.make_emit();
return a.exec();
}
The output is as follows:
main in thread QThread(0xc20180)
thread created, now in thread QThread(0x7fb9680009e0)
called make_emit in thread QThread(0xc20180)
doing something in thread QThread(0xc20180)
Now, as per my understanding, the following happens:
create_thread()
is called. The EmitCaller
object (QWebSocket
object) is created inside a new thread. Thus, the thread affinity of the object should be the new thread, and all signals sent from it should be from the new thread.connect()
is done using Qt::DirectConnection
. So, the slot do_something()
should be called in this new thread, even though its class object, b
, lies in the main thread.do_emit()
is called on the object whose thread affinity is with the new thread, which should result in the behaviour expected as described above.I expect the output to instead be:
main in thread QThread(0xc20180)
thread created, now in thread QThread(0x7fb9680009e0)
called make_emit in thread QThread(0xc20180)
doing something in thread QThread(0x7fb9680009e0)
A few additional points:
QWebSocket
object. However, the signal there is generated when a new client connects. To send the signal myself, I have created a pointer to access the object.std::thread
, I cannot use QThread
for this.Why is the slot being called in the receiver's thread, instead of the
emitter's thread, even though Qt::DirectConnection
is used?
If this is incorrect, where am I going wrong? (I am new to the signal-slot system of Qt).
If this cannot be done this way, how can I achieve the behaviour I want? I would like do_something()
to run in a separate thread.
Thank you.
Upvotes: 0
Views: 1167
Reputation: 11513
You have to consider 3 threads in your problem statement:
Queued/BlockingQueued connections ensure that the slot will be executed in the receivers thread.
DirectConnection executes the slot in the current thread. This is not always the thread the sender lives in! In fact, there is no standard way to enforce running a slot in the sender's thread (because usually, the receiver's thread is what you want).
NB: AutoConnection uses QueuedConnection if the current thread and receiver thread are not the same, otherwise DirectConnection
To solve your problem, you could forcably switch to the sender's thread if you're on another thread, like this:
In EmitCaller, add
private: Q_INVOKABLE my_thread_do_emit() { do_emit(); }
Then in the implemetation:
void EmitCaller::do_emit()
{
if (this->thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "my_thread_do_emit", Qt::BlockingQueuedConnection);
} else {
emit my_signal();
}
}
However, I would suggest that you reconsider your design. It seems unusual to call a slot in a certain foreign thread. Maybe there is a problem in your thread affinity setup... (e.g. the receiver should live in the newly created thread)
Upvotes: 4