Qt Call Slot Thread
- Qt Call Slot Thread Reducer
- Qt Call Slot In Different Thread
- Qt Call Slot From Another Thread
- Qt Run Slot In Thread
The main thing in this example to keep in mind when using a QThread is that it's not a thread. It's a wrapper around a thread object. This wrapper provides the signals, slots and methods to easily use the thread object within a Qt project. To use it, prepare a QObject subclass with all your desired functionality in it. Qt uses the timer's thread affinity to determine which thread will emit thetimeout signal. Because of this, you must start and stop the timer in its thread; it is not possible to start a timer from another thread. I have a main GUI thread where business objects are created. We call a custom method in the Worker thread instance with the size of the viewer label and the number of stars, obtained from the spin box. Whenever is star is drawn by the worker thread, it will emit a signal that is connected to the addImage slot. For years, Qt has sported an easy-to-use threading library, based around a class called QThread. As of Qt4, you can use QThread to start your own event loops. This might sound somewhat uninteresting at first, but it means you can have your own signals and slots outside the main thread. Qt's signals and slots mechanism ensures that if you connect a signal to a slot, the slot will be called with the signal's parameters at the right time. Signals and slots can take any number of arguments of any type. They are completely type safe.
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?
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
:
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:
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.
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:
Qt Call Slot Thread Reducer
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.
I wrote about multi-threading in Qt a while back, and how to use QThread to wrap a blocking function call “lock free”. Today we’ll talk about how to pass data into your thread. This approach can be used, for example, to tell your QThread to stop.
There are two ways to use QThread, with and without an event loop, and the preferred method for talking to a QThread depends on which of them you use.
Way 1: Without an event loop
Qt Call Slot In Different Thread
When you’re not using an event loop, the thread’s run() method often looks like this:
Since this method does not use an event loop, there is no way to deliver a signal to your thread. So if you want to pass data into the thread, you have to use a good old fashioned mutex. Let’s look at an example showing you how to stop your thread:
In this case, we have to check the stopRequested variable in a timely manner in our thread’s run() method. The longer you run between checks, the longer it will take your thread to actually stop.
Outside observers can use the finished() signal to know when your thread is actually done. So if you are in a QMainWindow, for example, and a closeEvent() happens, you can ignore the event, call MyThread::stop(), and then when the QThread::finished() signal arrives, you can actually close the window.
The downside is that the stop() call will actually block while it tries to acquire the mutex. Given the way this code is written, the blocking will probably be very short, but hey, I hate blocking. Let’s see if we can dig up a better way to do this.
Qt Call Slot From Another Thread
Way 2: With an event loop
If you have an event loop, you can use Qt’s meta-objects to talk to your thread. Let’s look at the same example as before, only this time with no locking or blocking.
Qt Run Slot In Thread
Look mom! No locks! Now we have killed our thread, safely and gracefully. There is no chance of blocking, and we learned something about QMetaObject.
A couple items to note:
- The doSomething() method is left as an exercise for the reader, but be careful about the QTimer interval. I used 0, which means it will be firing almost constantly.
- The stop() method must be a slot in MyThread (and not just a regular method) or else invokeMethod() will return false and not actually re-invoke stop() for you.
- You can pass arguments to your thread this way, but it requires a bit more fun with QMetaObject::invokeMethod().
- You can reduce this whole thing to a magical macro that you could put at the top of your stop() method, saving you from having to write if(currentThread() this) at the top of every method. Hint: use the __FUNCTION__ macro.
- To run this example code, you’ll need to #include these files: QThread, QTimer, QMetaObject, QMutexLocker, and QMutex
- To call quit(), it may not actually be necessary to be running on the same QThread (it works for me without the QMetaObject), but this will be required when you start passing in data to your thread. Without it, your program could do unpredictable naughty things. I can’t find anything in the docs about whether quit() is thread safe.
I’ve found this QMetaObject approach the most effective and easiest way to pass data to QThreads safely, without blocking and without locks.
Happy threading!