Qt Signal Slot Thread Context



Qt documentation states that signals and slots can be direct, queued and auto.

It also stated that if object that owns slot ‘lives’ in a thread different from object that owns signal, emitting such signal will be like posting message – signal emit will return instantly and slot method will be called in target thread’s event loop.

Unfortunately, documentation do not specify that ‘lives’ stands for and no examples is available. I have tried the following code:

main.h:

@sierdzio said in Cannot connect signal and slot from different thread.: Also, remember to use Qt::QueuedConnection for your inter-thread connections - then you don't have to worry about locking any mutexes and such. Thank you sierdzio for your help! Nd the index of the signal and of the slot Keep in an internal map which signal is connected to what slots When emitting a signal, QMetaObject::activate is called. It calls qt metacall (generated by moc) with the slot index which call the actual slot. Qt offers a new event handling system: signal-slot connections. Imagine an alarm clock. When alarm is ringing, a signal is being sent (emit). And you're handling it in a slot.

main.cpp:

Output is:

MySlot() is never called :(. What I’m doing wrong?

Answers:

There are quite a few problems with your code :

  • like said by Evan the emit keyword is missing
  • all your objects live in the main thread, only the code in the run methods live in other threads, which means that the MySlot slot would be called in the main thread and I’m not sure that’s what you want
  • your slot will never be called since the main event loop will never been launched : your two calls to wait() will only timeout after a very long time (and you’ll probably kill your application before that happens) and I don’t think that’s what you want either, anyway they really have no use in your code.

This code would most likely work (though I have not tested it) and I think it does what you want it to do :

Now MyObject will live in thread2 (thanks to moveToThread).

MySignal should be sent from thread1 (thought I’m not sure on that one, it might be sent from main thread, it doesn’t really matter).

Qt Signal Thread

No event loop is needed in thread1 since emitting a signal doesn’t need an event loop. An event loop is needed in thread2 (lanched by exec()) to receive the signal.

MySlot will be called in thread2.

Answers:

Do not subclass QThread for Qt 4.4+

While Aiua’s answer is good, I want to point out some issues with QThread and Qt 4.6 or 4.7.

This article sums it up: http://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/

Lack of Documentation on Qt’s part

Unfortunately the problem stems from a lack of updates to documentation. Prior to Qt 4.4 QThread had no default run() implementation, which meant that you had to subclass QThread in order to use it.

If you’re using Qt 4.6 or 4.7 then you almost certainly should not subclass QThread.

Use moveToThread

The key to getting slots to execute in a worker thread is to use the moveToThread method as Aiua pointed out.

Tags: qt

The thread that doesn't thread...

Lets say you have some background work you need to get done. The work is pretty time intensive, so you don't want to block the main UI thread while its executing. To do this work, you implement a worker thread like the one below to do the work once a second
On the surface this code seems sane. When the thread starts executing, we setup a QTimer thats going to run in the current thread's event queue. We connect our 'doWork' function to the timeout signal. Then we do work in the worker thread's context... right?
If you try this out with any serious work, though, you'll find the main thread is actually getting blocked by the work thats being done. The GUI running in the main thread will not be responsive. Its almost as if threading itself has stopped working.
So what's happening? Well there's two pieces of information we need to learn to figure this out. First is thread affinity. Thread affinity is described by QT as the 'thread the QObject lives in'. According to the QT docs, when a QObject is created, its 'thread' pointer is set to the current executing thread. Now here's the important question -- what's the current executing thread when we create our worker thread?
Qt Signal Slot Thread Context
Well it can't be the worker thread, as its not really created yet. The __init__ for the thread is run in the thread creating it. The QThread doesn't actually really become, well, a thread until you call start on it and it begins to run().
So a QThread's affinity is always the thread that creates it.
Ok the next piece of information is what happens when we connect the signal. By default, the connection is whats known as an AutoConnection. An AutoConnection begins by figuring out the thread affinity of the emitting and receiving QObjects. So what is the affinity of the QTimer firing the signal? Well its created while worker thread is running, so its the worker thread. Ok but the receiver, whats its affinity? Well the receiver is the QThread itself. We just established that the QThread's affinity is the thread that creates it. So we have a signal going from the QTimer living in the worker thread to the QThread living in the main thread.

AutoConnection posts the signal as an event to be queued in the receiving thread's event queue. The timeout signal is posted from the worker thread to the main thread. The main thread eventually gets to this signal, and figures out the slot to call, in this case the slot is doWork in our worker thread. The main thread then calls 'doWork' in its own event queue in response to receiving the signal.
The work is intensive. It takes a lot of time. And the main thread's event queue can't run because its doing the work the worker thread should be doing. So none of the GUI events get processed, and the main thread effectively becomes starved.

The solution?

We need to make sure that the QObject doing the work lives in the worker thread. So we need it to not be the QThread itself. We need something like
So great, notice we created the Worker while the worker thread is executing. Our Worker and the QTimer have the same thread affinity (that is they both live in the worker thread). Therefore, when timeout is fired, doWork is fired from the worker thread's thread context.
Problem solved!

Some caveats....

You might want to control how the worker is created before you create the worker thread. Maybe you want to pass the worker into the worker thread for example. But then wait, if we create it outside of this thread, its not living in the worker, and we have the same problem again! Aaaah!
Luckily QObject has a method moveToThread which takes a QThread, changing the affinity to the passed in thread. This method is intended to allow you to push (important to note pulling is not threadsafe) into another thread. So we can do this:
Of course, you probably don't want to retain the passed in worker in the creator of the thread. You might do call a method on it from outside the worker thread while its doing work in the worker thread's context. Most likely that operation will not be thread-safe and unintentional bugs will ensue. Ideally, you don't want to keep worker around in your worker thread's client code.
This is something that's easy to forget and hard to enforce, so another solution would be to pass in a factory function to create worker for you instead of passing the worker itself. This will remove the ability for the worker thread's owner/client to monkey with the worker in an unsafe way. So we'd then have something like this:

Qt Signal Slot Performance


Final thoughts

Its interesting that when researching all this I discovered that for QT 5, 'Subclassing QThread is no longer recommended way of using QThread'. Well that's not surprising. When you think about it, since QThread lives in the thread that creates it, methods of QThread itself are a pretty unsafe place to do any work. We know run is executing in another context, so we can setup our workers and what not there, but we can't be sure whats going on in other methods of QThread.
This is a little weird if you've ever dealt with other frameworks like MFC. For example, for CWinThread, you'd be used to attaching thread message handlers to methods of CWinThread for direct handling in that CWinThread's context. You get trained to expect methods of you derrived CWinThread are being executed in the CWinThread's context, not in some external context. Sure you can always call into a CWinThread from another thread's context, but MFC developers know its better to post to the other thread and have it deal with the event in its own context.

Qt Signal Slot Thread Contexto

Classes inheriting from QThread don't work that way. And QT signals/slots are different then Windows thread messages. So beware and keep your work out of QThread, delegating it to other objects that live in that QThread!