Signal Slot Qt Thread



A short history

  1. Signal Slot Qt Thread Dimensions
  2. Qt Signal Slot Thread Context

Long long ago, subclass QThread and reimplement its run() function is the only recommended way of using QThread. This is rather intuitive and easy to used. But when SLOTS and Qt event loop are used in the worker thread, some users do it wrong. So Bradley T. Hughes, one of the Qt core developers, recommend that use worker objects by moving them to the thread using QObject::moveToThread . Unfortunately, some users went on a crusade against the former usage. So Olivier Goffart, one of the former Qt core developers, tell the subclass users: You were not doing so wrong. Finally, we can find both usages in the documentation of QThread.

The second button lets you only start another thread (MyLongThread) that prints an asterisk in the stdout every second for 10 seconds. This example uses the api version 2 (introduced with PyQt 4.5) to connect signals to slots. #!/usr/bin/env python2.

  1. This might sound somewhat uninteresting at first, but it means you can have your own signals and slots outside the main thread. The Trolls created a new way to connect signals to slots such that signals can actually cross thread boundaries. I can now emit a signal in one thread and receive it in a slot in a different thread.
  2. Signals and Slots Across Threads Qt supports these signal-slot connection types: Auto Connection(default) If the signal is emitted in the thread which the receiving object has affinity then the behavior is the same as the Direct Connection. Otherwise, the behavior is the same as the Queued Connection.'
  3. Qt signal/slot implementation is thread safe, so that you can use it to send messages between different QThreads, this is especially important, as anything UI related should run in the main thread of Qt, anything that could block your UI should not run in this thread, so running jobs in a QThreadPool and emitting the finished result as a signal.
  4. The recommended way of working with threads in Qt has changed since the documentation was written. It is usually better not to add signals, let alone slots, to QThread. Instead, create a QObject (derived) instance, and call moveToThread on it to move it to the thread. Put your signals and slots in this worker object instead.

QThread::run() is the thread entry point

From the Qt Documentation, we can see that

A QThread instance represents a thread and provides the means to start() a thread, which will then execute the reimplementation of QThread::run(). The run() implementation is for a thread what the main() entry point is for the application.

As QThread::run() is the thread entry point, it is rather intuitive to use the Usage 1.

Usage 1-0

To run some code in a new thread, subclass QThread and reimplement its run() function.

For example

The output more or less look like:

Usage 1-1

As QThread::run() is the thread entry point, so it easy to undersand that, all the codes that are not get called in the run() function directly won't be executed in the worker thread.

In the following example, the member variable m_stop will be accessed by both stop() and run(). Consider that the former will be executed in main thread while the latter is executed in worker thread, mutex or other facility is needed.

The output is more or less like

You can see that the Thread::stop() is executed in the main thread.

Usage 1-2 (Wrong Usage)

Though above examples are easy to understand, but it's not so intuitive when event system(or queued-connection) is introduced in worker thread.

For example, what should we do if we want to do something periodly in the worker thread?

  • Create a QTimer in the Thread::run()
  • Connect the timeout signal to the slot of Thread

At first glance, the code seems fine. When the thread starts executing, we setup a QTimer thats going to run in the current thread's event queue. We connect the onTimeout() to the timeout signal. Then we except it works in the worker thread?

But, the result of the example is

Oh, No!!! They get called in the main thread instead of the work thread.

Very interesting, isn't it? (We will discuss what happened behined this in next blog)

How to solve this problem

In order to make the this SLOT works in the worker thread, some one pass the Qt::DirectConnection to the connect() function,

and some other add following line to the thread constructor.

Both of them work as expected. But ...

The second usage is wrong,

Even though this seems to work, it’s confusing, and not how QThread was designed to be used(all of the functions in QThread were written and intended to be called from the creating thread, not the thread that QThread starts)

In fact, according to above statements, the first workaround is wrong too. As onTimeout() which is a member of our Thread object, get called from the creating thread too.

Both of them are bad uasge?! what should we do?

Usage 1-3

As none of the member of QThread object are designed to be called from the worker thread. So we must create an independent worker object if we want to use SLOTS.

The result of the application is

Signal Slot Qt Thread Dimensions

Problem solved now!

Though this works perfect, but you may have notice that, when event loop QThread::exec() is used in the worker thread, the code in the QThread::run() seems has nothing to do with QThread itself.

So can we move the object creation out of the QThread::run(), and at the same time, the slots of they will still be called by the QThread::run()?

Usage 2-0

If we only want to make use of QThread::exec(), which has been called by QThread::run() by default, there will be no need to subclass the QThread any more.

Qt signal slot cross thread
  • Create a Worker object
  • Do signal and slot connections
  • Move the Worker object to a sub-thread
  • Start thread

The result is:

As expected, the slot doesn't run in the main thread.

In this example, both of the QTimer and Worker are moved to the sub-thread. In fact, moving QTimer to sub-thread is not required.

Simply remove the line timer.moveToThread(&t); from above example will work as expected too.

The difference is that:

In last example,

  • The signal timeout() is emitted from sub-thread
  • As timer and worker live in the same thread, their connection type is direct connection.
  • The slot get called in the same thead in which signal get emitted.

While in this example,

  • The signal timeout() emitted from main thread,
  • As timer and worker live in different threads, their connection type is queued connection.
  • The slot get called in its living thread, which is the sub-thread.

Thanks to a mechanism called queued connections, it is safe to connect signals and slots across different threads. If all the across threads communication are done though queued connections, the usual multithreading precautions such as QMutex will no longer need to be taken.

In short

  • Subclass QThread and reimplement its run() function is intuitive and there are still many perfectly valid reasons to subclass QThread, but when event loop is used in worker thread, it's not easy to do it in the right way.
  • Use worker objects by moving them to the thread is easy to use when event loop exists, as it has hidden the details of event loop and queued connection.

Reference

  • http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
  • http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
  • http://ilearnstuff.blogspot.com/2012/08/when-qthread-isnt-thread.html

This blog is part of a series of blogs explaining the internals of signals and slots.

In this article, we will explore the mechanisms powering the Qt queued connections.

Summary from Part 1

In the first part, we saw that signalsare just simple functions, whose body is generated by moc. They are just calling QMetaObject::activate, with an array of pointers to arguments on the stack.Here is the code of a signal, as generated by moc: (from part 1)

QMetaObject::activatewill then look in internal data structures to find out what are the slots connected to that signal.As seen in part 1, for each slot, the following code will be executed:

So in this blog post we will see what exactly happens in queued_activateand other parts that were skipped for the BlockingQueuedConnection

Qt Event Loop

A QueuedConnection will post an event to the event loop to eventually be handled.

When posting an event (in QCoreApplication::postEvent),the event will be pushed in a per-thread queue(QThreadData::postEventList).The event queued is protected by a mutex, so there is no race conditions when threadspush events to another thread's event queue.

Once the event has been added to the queue, and if the receiver is living in another thread,we notify the event dispatcher of that thread by calling QAbstractEventDispatcher::wakeUp.This will wake up the dispatcher if it was sleeping while waiting for more events.If the receiver is in the same thread, the event will be processed later, as the event loop iterates.

The event will be deleted right after being processed in the thread that processes it.

An event posted using a QueuedConnection is a QMetaCallEvent. When processed, that event will call the slot the same way we call them for direct connections.All the information (slot to call, parameter values, ...) are stored inside the event.

Copying the parameters

Signal

The argv coming from the signal is an array of pointers to the arguments. The problem is that these pointers point to the stack of the signal where the arguments are. Once the signal returns, they will not be valid anymore. So we'll have to copy the parameter values of the function on the heap. In order to do that, we just ask QMetaType. We have seen in the QMetaType article that QMetaType::create has the ability to copy any type knowing it's QMetaType ID and a pointer to the type.

To know the QMetaType ID of a particular parameter, we will look in the QMetaObject, which contains the name of all the types. We will then be able to look up the particular type in the QMetaType database.

queued_activate

We can now put it all together and read through the code ofqueued_activate, which is called by QMetaObject::activate to prepare a Qt::QueuedConnection slot call.The code showed here has been slightly simplified and commented:

Upon reception of this event, QObject::event will set the sender and call QMetaCallEvent::placeMetaCall. That later function will dispatch just the same way asQMetaObject::activate would do it for direct connections, as seen in Part 1

BlockingQueuedConnection

BlockingQueuedConnection is a mix between DirectConnection and QueuedConnection. Like with aDirectConnection, the arguments can stay on the stack since the stack is on the thread thatis blocked. No need to copy the arguments.Like with a QueuedConnection, an event is posted to the other thread's event loop. The event also containsa pointer to a QSemaphore. The thread that delivers the event will release thesemaphore right after the slot has been called. Meanwhile, the thread that called the signal will acquirethe semaphore in order to wait until the event is processed.

It is the destructor of QMetaCallEvent which will release the semaphore. This is good becausethe event will be deleted right after it is delivered (i.e. the slot has been called) but also whenthe event is not delivered (e.g. because the receiving object was deleted).

A BlockingQueuedConnection can be useful to do thread communication when you want to invoke afunction in another thread and wait for the answer before it is finished. However, it must be donewith care.

The dangers of BlockingQueuedConnection

You must be careful in order to avoid deadlocks.

Obviously, if you connect two objects using BlockingQueuedConnection living on the same thread,you will deadlock immediately. You are sending an event to the sender's own thread and then are locking thethread waiting for the event to be processed. Since the thread is blocked, the event will never beprocessed and the thread will be blocked forever. Qt detects this at run time and prints a warning,but does not attempt to fix the problem for you.It has been suggested that Qt could then just do a normal DirectConnection if both objects are inthe same thread. But we choose not to because BlockingQueuedConnection is something that can only beused if you know what you are doing: You must know from which thread to what other thread theevent will be sent.

The real danger is that you must keep your design such that if in your application, you do aBlockingQueuedConnection from thread A to thread B, thread B must never wait for thread A, or you willhave a deadlock again.

When emitting the signal or calling QMetaObject::invokeMethod(), you must not have any mutex lockedthat thread B might also try locking.

A problem will typically appear when you need to terminate a thread using a BlockingQueuedConnection, for example in thispseudo code:

You cannot just call wait here because the child thread might have already emitted, or is about to emitthe signal that will wait for the parent thread, which won't go back to its event loop. All the thread cleanup information transfer must only happen withevents posted between threads, without using wait(). A better way to do it would be:

The downside is that MyOperation::cleanup() is now called asynchronously, which may complicate the design.

Qt Signal Slot Thread Context

Conclusion

This article should conclude the series. I hope these articles have demystified signals and slots,and that knowing a bit how this works under the hood will help you make better use of them in yourapplications.