Signal Slot Qthread



A short history

For signals, the thread affinity doesn’t matter. But for slots it matters a lot. Subclassing QThread. Now you can perhaps appreciate why subclassing QThread can be a problem. If you have anything using slots in the subclass objects, the thread affinity will mean that it will use the original thread.

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.

QThread::run() is the thread entry point

From the Qt Documentation, we can see that

执行一个耗时的操作时,多线程是常用的选择,最常见的一个方式或许是继承QThread,然后实现其virtual void run函数,又或者使用高级类,比如QtConcurrent。总之,好像“回字的四种写法”,当然不同情况下,每种. QML Beginners: Core Beginners: https://www.udemy.com/course/qt-core-for-b. Sender's signal is serialized into an event Event is posted to the receiver's event queue Event is deserialized and the slot is executed Communication between threads is easy! This is why QThread self-affinity is usually wrong. It implies you want to send cross-thread signals to yourself.

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

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.

  • 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,

Signal
  • 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

Qthread Signal Slot Example

  • 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

Review...

In When a QThread isn't a thread, we learned some of the finickiness in how QThread needs to be used. QT has a concept of 'thread affinity' -- what thread the QObject 'lives' in. At construction, a QObject becomes tied to the current running thread. Naturally, this applies to QThread as well. As of course our new thread cannot be running during its own creation, the new QThread instance is created while another 'parent thread' is running. Because of the thread affinity rules, it now lives in the parent thread. This association with the parent thread combined with how QT signals-and-slots work creates a problematic situation. When emitting a signal, QT is smart enough figure out based on the destination/source QObject's thread affinity whether the signal's connected slots should be called directly as methods (these QObjects share thread affinity) or whether QT should post to the associated destination QThread's event queue for processing in the correct context (these QObjects that don't share a thread affinity).
Put all this together, and you'll see a problem. Since a QThread's affinity is the thread that created it, slots executing within it (that is methods of your QThread) will execute in the QThread's parent thread's context when responding to signals. In other words, if you 'post' to a QThread method via a signal, you're not doing work in that QThread as you'd probably expect. A QThread isn't like, say, a CWinThread where methods in your thread tend to be handling thread messages and run in your thread's context.
To illustrate, in the code below, doWork does not execute in the MyWorkerThread's context.The worker thread is effectively meaningless as the meat of the work is being done in the worker thread's parent thread.

Solution 0.1

The solution I proposed was to do all your required work in some secondary QObject (worker below). You can then change that thread's affinity to the worker thread by calling the very handy 'moveToThread' method (see __init__ below). Now that both QObjects are associated with MyWorkerThread, the work done on the timer signal is done in MyWorkerThread's context.

There's a better way...

Since writing the original post, I've read several illuminating articles that discuss this topic. Specifically The Great QThread Mess and Using QThread Without Subclassing.In short, they describe the same problem I do and go on to describe the sad state of QThread documentation. See as it turns out you don't have to subclass QThread.
I had been overriding run because I thought I needed to call self.exec_() directly to setup an event queue. It appeared It turns out that the default implementation of run gives you an event queue! So an even better solution is to do something that doesn't subclass QThread. Something like:

We simply create two QObjects that will communicate via signals-and-slots, create a vanilla QThread, assign the thread affinity of the new objects to that QThread, and voila! All work will be done in the worker thread's context. Moreover, any signal emited from anywhere will be handled in the worker's thread context.

QThread with new hotness best practices

This turns out that this is a neat way to setup work. It creates a bit of a danger zone though. In my 0.1 solution, the QThread provided a nice container for holding objects that other thread's code shouldn't play with. Here though, everything is wide open in plain sight in client code.
Despite the awesomeness above, I tend to not want to have the object's I've just pushed into the new worker to hang around as now its code is potentially executing in two thread contexts and at the very least my 'worker' is probably not thread-safe. For example, some work might be getting done in the worker thread's context via a signal and I may accidentally call a method directly on worker or timer. That all being said, I still tend to want to control how the worker is created. I may need to construct it with specific parameters describing the work to be done. I might need to connect it to some signals I or it might emit to facilitate communication across threads.
As such, I've generally started to follow something like this pattern for my own safety and sanity

Qt Signal Slot Qthread


Notice under the 'moveToThread' calls we store timer and worker as attributes of our QThread. I find this a handy way to keep the objects alive so they're not garbage collected (maybe that's paranoia on my part, but they seem to represent a cycle with nothing named in the code pointing to them). So I like to stash them away in my QThread.
In client code I would do something like:
Notice we forget about worker once we've started it. If for some reason I need to change something about 'worker' we can't talk to it directly. But that's ok. We've setup some useful signals/slots to communicate between us and the worker. Since the worker is associated with workerThread and our client QObject is presumably not, we can rely on QT's signals and slots to safely post the signal's between the two QObject's thread queues safely.

Qthread Signal Slot

Conclusion

I could keep getting anal about more finicky details of this code and ways to improve, but this is just the basic sketch of what seems to be a sane pattern for working with QThreads. Once you get the hang of it and realize you don't and shouldn't subclass QThread, life becomes a lot more straightforward and easier. Simply put, never do work in QThread. You should almost never need to override

Qthreadpool Signal Slot

run. For most use cases, setting up proper associations with a QObject to a QThread and using QT's signals/slots creates an extremely powerful way to do multithreaded programming. Just be careful not to let the QObjects you've pushed to your worker threads hang around as certain hilarity will almost definitely ensue.