Qt Call Slot Thread

broken image


Last time I explained how plain C++ objects and their methods interact with threads. This time around, we will look at QObjects, and their 'thread affinity'.

Now that we know object methods can be accessed by any thread at any time,we'll consider the situation from the point of view of Qt.

What are QObjects?

Qt is a great framework, and at its heart areQObjects.Through Qt's moc compilerextra functionality is seemlessly added to C++ objects.The two most notable additions are signals and slots for inter-object communication, and events.These allow great flexibility and modularity, but how do they work in the context of threads?

Slot

Qt Call Slot Thread Tool

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. 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. Lets explain it more, Each thread created by Qt (including main thread and new threads created by QThread) have Event loop, the event loop is responsible for receiving signals and call aproporiate slots in its thread. The Qt signals/slots and property system are based on the ability to introspect the objects at runtime. Introspection means being able to list the methods and properties of an object and have all kinds of information about them such as the type of their arguments. QtScript and QML would have hardly been possible without that ability. Cross-thread signal-slot connections are implemented by dispatching a QMetaCallEvent to the target object. A QObject instance can be moved to a thread, where it will process its events, such as timer events or slot/method calls. To do work on a thread, first create your own worker class that derives from QObject. Then move it to the thread. Qt's event systemis very useful for inter-thread communication. Every thread may have its own event loop. To call a slot (or any invokablemethod) in another thread, place that call in the target thread's event loop.

Call
Slot

For example, if an event or slot is triggered in a QObject (whichultimately triggers a function), which thread is calling that function? Theanswer lies in thread affinity. But let's back up a bit.

Qt threads and Event loops

Having the ability to asynchronously trigger functions, and raise/handle eventsmeans that Qt must have some kind of an event loop. An event loop will continuallymonitor a queue of events to be handled, and dispatch them accordingly.Indeed, every QThread has a built-in event loop that can be entered.

One way to see this directly is by inheriting from QThread:

Qt Call Slot Thread Reducer

The above is a good example for demonstration, but is rarely done in production. We will see a better way to run custom code on QThreads in the next section.

In particular the GUI thread (the main thread), also has an event loop which islaunched by calling QApplication::exec(), which only returns after the user has quit the program.

So far the most important thing to remember is:

Qt Call Slot Thread Holders

Slot

Qt Call Slot Thread Tool

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. 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. Lets explain it more, Each thread created by Qt (including main thread and new threads created by QThread) have Event loop, the event loop is responsible for receiving signals and call aproporiate slots in its thread. The Qt signals/slots and property system are based on the ability to introspect the objects at runtime. Introspection means being able to list the methods and properties of an object and have all kinds of information about them such as the type of their arguments. QtScript and QML would have hardly been possible without that ability. Cross-thread signal-slot connections are implemented by dispatching a QMetaCallEvent to the target object. A QObject instance can be moved to a thread, where it will process its events, such as timer events or slot/method calls. To do work on a thread, first create your own worker class that derives from QObject. Then move it to the thread. Qt's event systemis very useful for inter-thread communication. Every thread may have its own event loop. To call a slot (or any invokablemethod) in another thread, place that call in the target thread's event loop.

For example, if an event or slot is triggered in a QObject (whichultimately triggers a function), which thread is calling that function? Theanswer lies in thread affinity. But let's back up a bit.

Qt threads and Event loops

Having the ability to asynchronously trigger functions, and raise/handle eventsmeans that Qt must have some kind of an event loop. An event loop will continuallymonitor a queue of events to be handled, and dispatch them accordingly.Indeed, every QThread has a built-in event loop that can be entered.

One way to see this directly is by inheriting from QThread:

Qt Call Slot Thread Reducer

The above is a good example for demonstration, but is rarely done in production. We will see a better way to run custom code on QThreads in the next section.

In particular the GUI thread (the main thread), also has an event loop which islaunched by calling QApplication::exec(), which only returns after the user has quit the program.

So far the most important thing to remember is:

Qt Call Slot Thread Holders

Threads in Qt handle asynchronous events, and thus all have an event-loop.

QObjects and QThreads

Now we come to the meat of this post- if C++ objects can be accessed by anythread, then what thread is handling the events of a particular QObject?The answer is that whenever a QObject is created, it is assigned a parent threadwhich handles all of it's events and slot invocations- it has a threadaffinity. Whichever thread it was created in, becomes it's parent thread!

This is where the confusion came about for me. On the one hand C++ objectmethods can be called from any thread at any time, while QObjects (themselvesC++ objects) have a parent thread which handles its events. As you can hopefullysee, there is no conflict:

QObject methods can be called from any thread at any time, just like a C++object. In addition, a parent thread is assigned to handle anyasynchronous events and slot invocations.

So there are two ways for a function to be called on a QObject:

  • Directly from any thread
  • Indirectly by invoking a connected slot or raising an event. This posts an event onto the parent thread's event loop, which eventually calls the function in question.

Qt Call Slot Thread Gages

To complete this article, let's look at running our code on other threads.As promised before we will not inherit from QThread for the job.If we can't customize a thread, and QObjects are bound to the thread that created them, how can we achieve this? Qt allows users to moveQObjects to other threads, thereby changing the thread affinity to the new thread:

This is much simpler and easier to follow than subclassing a QThread each time you want to create a worker thread.Thanks to Jurily for suggesting this in a reddit comment.

I hope you enjoyed this simplified rundown of QObjects and threads! More in-depth documentation can be found on the Qt-project website.





broken image