Qt signals (QueuedConnection and DirectConnection)
Asked Answered
S

3

60

I'm having trouble with Qt signals.

I don't understand how DirectConnection and QueuedConnection works?

I'd be thankful if someone will explain when to use which of these (sample code would be appreciated).

Stealth answered 24/2, 2013 at 12:7 Comment(4)
See #11230580 and qt-project.org/doc/qt-4.8/threads-qobject.htmlCollective
What specifically don't you understand about these? Your question boils down to "how do signals & slots work" without that, which is a bit broad and well covered by the docs.Rudin
Hmm.. as far as I understand, QueuedConnection should be used when sender and receiver are in different threads. For example, I have GUI Thread(Main thread) and the new thread (pThread) which has signal for example void doSomething(); and receiver is main GUI thread. So, I have to use QueuedConnection never mind where I will call it ? (in GUI thread or new thread, the connect command) Thank you..Stealth
Read the docs in the first comment. Unless you're trying to do something very specific and you fully understand the risks, don't specify a connect mode at all. The default will use the right mode (direct for intra-thread, queued for inter-thread).Rudin
C
103

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).

Cradle answered 13/3, 2013 at 2:29 Comment(7)
@Jacob so is it not better to directly call handleChange() like a normal function instead of going into the SIGNAL SLOT confusion for the Direct Connection case?Eos
Cool_Coder: The advantage to invoking the method via sig/slot connection is that you don't have to build knowledge of object B into A - you can let the user of the two classes determine what should happen when somethingChanged is emitted from A. Does that make sense?Cradle
what happens if I dont specify Qt::QueuedConnection as a param ? The reason I ask this is that in many places specifying Qt::QueuedConnection or direct is a optional param. Does it mean that Qt takes care of the decision itself whether to "enqueue the signal or call direct" ?Judsen
@Game_Of_Threads : The default argument is Qt::AutoConnection. If the emitter & receiver are in the same thread, a DirectConnection is used. Otherwise, a QueuedConnection is used.Cradle
@JacobRobbins Would you please clarify why we don't need to use locks or synchronization when using Qt::QueuedConnection? Even if two threads share some data (that both may read or write)? Or when thread A pass some data through arguments of a signal that is connected to a slot in thread B?Kathykathye
@Kathykathye If threads share data, yes, you would need to synchronize. So, don't do that unless you absolutely have to. Using QueuedConnection allows users from many threads to invoke slots on a QObject without having to worry about whether or not that object's methods are re-entrant (because the event loop serializes the invocations). Does that make sense?Cradle
@JacobRobbins Yes, it does. Thanks. Actually, I am working on a project where I handle serial port communications in a worker thread. The user can change the serial port settings (e.g. baud rate) and I wanted to let the worker thread know about this change. But I think no data sharing happens here. So just using signal/slots should work fine. The use of mutex came to my mind because of an example in Qt docs. But I think the way of handling serial communication in that example differs from using Qt::QueuedConnection.Kathykathye
R
36

in addition to Jacob Robbins answer:

the statement "You won't see much of a difference unless you're working with objects having different thread affinities" is wrong;

emitting a signal to a direct connection within the same thread will execute the slot immediately, just like a simple function call.

emitting a signal to a queued connection within the same thread will enqueue the call into the threads event loop, thus the execution will always happen delayed.

QObject based class has a queued connection to itself

Rab answered 12/10, 2013 at 12:3 Comment(2)
I totally understood signals and slots now, thanks for mentioned it anyways.Stealth
Good point. I always let Qt choose the connection method unless I have a good reason to choose one or the other...so I get a direct conn. when the objects are on the same thread (using 4.8). It definitely makes a difference if you force a queued connection. I edited my answer to clarify.Cradle
A
2

Jacob's answer is awesome. I'd just like to add a comparative example to Embedded Programming.

Coming from an embedded RTOS/ISR background, it was helpful to see the similarities in Qt's DirectConnection to Preemptive behavior of the ISRs and Qt's QueuedConnection to Queued Messages in an RTOS between tasks.

Side note: Coming from an Embedded background, it's difficult for me to not define the behavior in the programming. I never leave the argument as Auto, but that is just a personal opinion. I prefer everything to be explicitly written, and yes that gets difficult at times!

Acarus answered 29/6, 2017 at 15:8 Comment(2)
I'm not entirely convinced that it is always better to explicitly choose direct versus queued connections. In an environment where things move quickly, the time it takes to go back and adjust these as the threading model evolves would make it a non-zero probability that a direct connection could end up being specified for a cross-thread signal and eventually cause intermittent crashes on some machine somewhere (race condition on faster or slower processor, etc.). Just saying that it adds risk during development.Carbonaceous
I respect that position, but my opinion is that I want to know if a behavior is changed after a module is written and for me the easiest way is for the compiler to blip during compilation. By explicitly defining the variable I would know as soon as the behavior was changed. So yes, it can slow down development but I prefer it over months later finding out a behavior has changed and doesn't work with the original design.Acarus

© 2022 - 2024 — McMap. All rights reserved.