Qt Signals (Queuedconnection and Directconnection)

Qt signals (QueuedConnection and DirectConnection)

You won't see much of a difference unless you're working with objects having different thread affinities. Let's say you have QObjects A and B and they're both attached to different threads. A has a signal called somethingChanged() and B has a slot called handleChange().

If you use a direct connection

connect( A, SIGNAL(somethingChanged()), B, SLOT(handleChange()), Qt::DirectConnection );

the method handleChange() will actually run in the A's thread. Basically, it's as if emitting the signal calls the slot method "directly". If B::handleChange() isn't thread-safe, this can cause some (difficult to locate) bugs. At the very least, you're missing out on the benefits of the extra thread.

If you change the connection method to Qt::QueuedConnection (or, in this case, let Qt decide which method to use), things get more interesting. Assuming B's thread is running an event loop, emitting the signal will post an event to B's event loop. The event loop queues the event, and eventually invokes the slot method whenever control returns to it (it being the event loop). This makes it pretty easy to deal with communication between/among threads in Qt (again, assuming your threads are running their own local event loops). You don't have to worry about locks, etc. because the event loop serializes the slot invocations.

Note: If you don't know how to change a QObject's thread affinity, look into QObject::moveToThread. That should get you started.

Edit

I should clarify my opening sentence. It does make a difference if you specify a queued connection - even for two objects on the same thread. The event is still posted to the thread's event loop. So, the method call is still asynchronous, meaning it can be delayed in unpredictable ways (depending on any other events the loop may need to process). However, if you don't specify a connection method, the direct method is automatically used for connections between objects on the same thread (at least it is in Qt 4.8).

QT signals and slots direct connection behaviour in application with a single thread

I think you are misunderstanding not the queued and direct versions, but the whole signal/slot mechanism as a whole. The signal/slot system is a more elegant solution to the callback problem, when you synchronously or asynchronously need to know when another part of your application (this may be single or multi-threaded) is done doing its work.

Direct / Queued

Let´s get this out of the way before diving into the basics.

  • If it is a direct connection, your code is ALWAYS executed on a single thread, regardless as to whether or not the slot is running on a different thread. (In most cases a VERY BAD idea)
  • If the connection is queued and the slot is running inside the same thread, it acts exactly like a direct connection. If the slot is running on a different thread, the slot will execute within its proper thread context that it is running in, so making the code execution multi-threaded.
  • If the connection is auto which is the default, for a good reason, then it will chose the appropriate connection type.

Now there is a way to force your code execution to jump into a slot in another thread, that is by invoking a method:

QMetaObject::invokeMethod( pointerToObject*, "functionName", Qt::QueuedConnection);

The Basics

So let´s go over the signal/slot mechanism quickly. There are three actors in the system : signals, slots and connect()

Signals are basically messages that say "Hey! I am finished". Slots are places in your code where post-processing happens, so once the signals are finished you can do something in the slot part of your code.

connect() is the way to organise what happens where and for what reason. The order of execution for signals/slots is the same as your code execution order. First come first serve, since you did mention single-threaded. In a multi-threaded environment that´s different. Honestly the order of execution shouldn´t matter, if you are trying to work with signals/slots and need a guaranteed order of execution you are designing your application wrong. Ideally you use the signal/slot mechanism in the same way that functional programming works, you pass messages along to the next instance to work on the data.

Enough ranting, let´s go down to some practical specifics:

signals:
void a();
void b();
slots:
void sa();
void sb();

Case 1

connect( a -> sa );  // Simplified Notation. Connect signal a to slot sa
connect( b -> sb );

:: emit a(); -> sa is executed
:: emit b(); -> sb is executed

Case 2

connect ( a -> sa );
connect ( a -> sb );
connect ( b -> sb );

:: emit a(); -> sa & sb are executed
:: emit b(); -> sb is executed

I think this should make it clear. If you have anymore questions let me know

Difference between Qt::QueuedConnection and Qt::DirectConnection when connecting a signal to a lambda

Here's our class SenderObject:

class Sender : public QObject {
Q_OBJECT
public:
explicit Sender() : QObject() {
_timer.setInterval(1000);
connect(&_timer, &QTimer::timeout, this,
[this] { emit somethingsChanged(); });
_timer.start();
}

signals:
void somethingsChanged();

private:
QTimer _timer;
};

Since there is no overload of QObject::connect that will take a lambda and a Qt connection type parameter, Snippet2 will execute in the sender's thread, which is a worker thread.

In order to force the lambda to execute on the main thread, here is a workaround:

int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThread t;
SenderObject senderObject = new SenderObject();
senderObject.moveToThread(&t);
QObject::connect(senderObject, &SenderObject::somethingsChanged, new QObject,
[]{qDebug << "Hello, world";}, Qt::QueuedConnection);
t.start();
return a.exec();
}

Since new QObject is called on the main thread, the lambda will also get executed on the main thread.

Or, if we don't like to see new, this is what will work:

int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThread t;
SenderObject senderObject = new SenderObject();
senderObject.moveToThread(&t);
QObject object;
QObject::connect(senderObject, &SenderObject::somethingsChanged, &object,
[]{qDebug << "Hello, world";}, Qt::QueuedConnection);
t.start();
return a.exec();
}

Or, even a better solution will be to use qApp, which is an instance of the application and a singleton and available from anywhere.

int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThread t;
SenderObject senderObject = new SenderObject();
senderObject.moveToThread(&t);
QObject::connect(senderObject, &SenderObject::somethingsChanged, qApp,
[]{qDebug << "Hello, world";}, Qt::QueuedConnection);
t.start();
return a.exec();
}

Qt 4.8 : connection behavior between two signals and one slot from different threads

The connection type between signals determines the thread where the second signal is emitted, just think of the signal as another function/slot that executes slots it is connected to (the exact same rules apply):

  • If the type is Qt::DirectConnection, the second signal is always emitted from the thread that emitted the first signal.
  • if the type is Qt::QueuedConnection, the second signal is always queued to be invoked when control returns to the event loop of the receiver object's thread.
  • If the type is Qt::AutoConnection, the connection type is resolved when the signal is emitted and the thread of the sending object is ignored.

    • If the receiver object lives in the same thread where the first signal is emitted, this will be the same as using Qt::DirectConnection.
    • Otherwise, this will be the same as using Qt::QueuedConnection.

I wrote a minimal test to demonstrate this thing:

#include <QtCore>

//QThread wrapper for safe destruction
//see http://stackoverflow.com/a/19666329
class Thread : public QThread{
using QThread::run; //final
public:
Thread(QObject* parent= nullptr): QThread(parent){}
~Thread(){ quit(); wait();}
};

class Worker : public QObject{
Q_OBJECT
public:
explicit Worker(QString name, QObject* parent= nullptr):QObject(parent){
setObjectName(name);
//the statement is printed from the thread that emits the signal
//since we don't provide a context object
connect(this, &Worker::workerSignal, [=]{
qDebug() << objectName() << "signal emitted from thread:"
<< QThread::currentThread()->objectName();
});
}
~Worker() = default;

Q_SIGNAL void workerSignal();
Q_SLOT void workerSlot(){
qDebug() << objectName() << "slot invoked in thread:"
<< QThread::currentThread()->objectName();
}
};

int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);

//using the main thread as threadA
QThread::currentThread()->setObjectName("threadA");
Worker workerA("workerA");
//creating threadB and threadC
Thread threadB;
threadB.setObjectName("threadB");
Worker workerB("workerB");
workerB.moveToThread(&threadB);
Thread threadC;
threadC.setObjectName("threadC");
Worker workerC("workerC");
workerC.moveToThread(&threadC);
threadB.start(); threadC.start();

//change the following types to whatever case you want to test:
auto sig2sig= Qt::QueuedConnection;
auto sig2slot= Qt::QueuedConnection;
qDebug() << "sig2sig= " << sig2sig << ", sig2slot=" << sig2slot;
QObject::connect(&workerA, &Worker::workerSignal,
&workerB, &Worker::workerSignal, sig2sig);
QObject::connect(&workerB, &Worker::workerSignal,
&workerC, &Worker::workerSlot, sig2slot);
emit workerA.workerSignal();

//quit application after 0.5 second
QTimer::singleShot(500, &a, &QCoreApplication::quit);
return a.exec();
}

#include "main.moc"

this will setup connections as follows:

workerA::workerSignal() -------> workerB::workerSignal() -------> workerC::workerSlot()

Each worker lives in its own thread, and you can change the connection types by changing the values assigned to sig2sig and sig2slot variables. Here is the output in the cases you asked for:

  • sig2sig = DirectConnection, sig2slot= DirectConnection:

    Everything is executed in threadA as direct function calls.

    "workerA" signal emitted from thread: "threadA"
    "workerB" signal emitted from thread: "threadA"
    "workerC" slot invoked in thread: "threadA"
  • sig2sig = DirectConnection, sig2slot= QueuedConnection:

    The signal is executed in threadA as a direct function call. The slot is invoked in threadC.

    "workerA" signal emitted from thread: "threadA"
    "workerB" signal emitted from thread: "threadA"
    "workerC" slot invoked in thread: "threadC"
  • sig2sig = QueuedConnection, sig2slot= DirectConnection:

    The signal is queued and its gets emitted from threadB. The slot is called in threadB as a direct function call.

    "workerA" signal emitted from thread: "threadA"
    "workerB" signal emitted from thread: "threadB"
    "workerC" slot invoked in thread: "threadB"
  • sig2sig = QueuedConnection, sig2slot= QueuedConnection:

    The signal is queued and its gets emitted from threadB. The slot invocation is also queued and gets executed in threadC. So, every thing happens in the right thread, this would be the same behavior if Qt::AutoConnection is used:

    "workerA" signal emitted from thread: "threadA"
    "workerB" signal emitted from thread: "threadB"
    "workerC" slot invoked in thread: "threadC"

Good Practice to pass arguments in queued connection

If you want a queued connection, you need to call connect with a 5. parameter Qt::QueuedConnection. Otherwise, you get a direct connection inside the thread where you sent the signal from. Edit: See Tobys comment below.

You must wrap a QVector<> into a typedef, otherwise registering will not work (bug? in Qt from the stoneage). Also do not use references to your typedef, will not work either.

Header

typedef struct {
int a;
int b;
} mystruct;

typedef QVector<mystruct> myvector;
Q_DECLARE_METATYPE(myvector);

Source

void MainWindow::test()
{
qRegisterMetaType<myvector>();

connect(this, SIGNAL(sigRec(myvector)), SLOT(slotRec(myvector)), Qt::QueuedConnection);
mystruct x = {1,2};
myvector v;
v.append(x);
emit sigRec(v);
}

void MainWindow::slotRec(myvector s)
{
}

Direct connection and event loop

When your signal and slot are in the same thread (as mentioned by user3183610) your signal will be direct connection (the default for same-thread). This effectively runs similarly to a function call. The signal is not queued, but instead the slot that the signal is connected to executes immediately - so you can think of it as a direct function call to the slot.

You can, however, change this behavior by using the Qt::QueuedConnection optional parameter at the end of your connect call:

connect(class_A_object,SIGNAL(signal_A()),class_B_object,on_signal_received, Qt::QueuedConnection);

This will force the use of the queue, your signal will be queued in the event loop and then other pending signals will be executed in order (this is often more desirable then DirectConnection because you can more easily guarantee the order of events). I tend towards to use of queued connections for this reason (though I believe direct is very slightly more efficient).

So for your code there is no return to the event loop until after on_button_click(). During on_button_click() you emit the diret signal signal_x() and immediately on_signal_received() is called (by-passing the event loop), when this finishes it returns back to on_button_click() - just like a function call would :)

Confusing behaviour of signals and slots depending on type of connection

Which is not. I thought, slots would be executed after maps are finished, but they are not executed at all. Why? What happened?

The slots gets executed when the control get back to the event loop, but here the startBlockingMap function gets called from the event loop, so the slots gets executed when the startBlockingMap function returns and the control get back to the event loop.

Why output when connection type is Qt::AutoConnection differs from output when connection type is direct and when it is queued? Why is it so random?

The blockingMap function use different threads to call your lambda, sometimes a thread from a threadpool, sometimes the thread that executes the blockingMap function(here the 'main' thread). You can check that by adding the line qDebug() << this->thread() << ' ' << QThread::currentThread(); into the lambda. Now the emit slot() gets executed sometimes from a function that is not the owner of the object, so the signal gets queued, sometimes from the 'main'-thread that is the owner of the object, so the slot gets executed directly and you can see the increase in the console.

Qt: Specifying multiple connection types with QObject::connect

Is it possible to specify that in one statement ?

In theory, it seems to be possible for your scenario. At least, you can read the docs documentation about it.

Qt::UniqueConnection 0x80 This is a flag that can be combined with any one of the above connection types, using a bitwise OR. When Qt::UniqueConnection is set, QObject::connect() will fail if the connection already exists (i.e. if the same signal is already connected to the same slot for the same pair of objects). This flag was introduced in Qt 4.6.

Based on the aforementioned documentation, you would write something like this:

#include <QCoreApplication>

int main(int argc, char **argv)
{
QCoreApplication coreApplication(argc, argv);
QObject::connect(&coreApplication, SIGNAL(aboutToQuit()), &coreApplication, SLOT(quit()), static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
return coreApplication.exec();
}


Related Topics



Leave a reply



Submit