NOTE: the quality level of this post is beta. I created this test program simply to test various combinations of QThread signal-slot operation. See the reading list for the proper way to use QThread.
Testing Qt Thread Operation
The Qt_ThreadTest application shows how Qt Signals and Slots can interact with the QThread object. According the the Qt documentation, a worker object should perform thread work operations and should be independent from the QThread object and the GUI object. This example shows 3 ways of implementing QThread and 3 ways of sending signals to the thread, none of which are considered correct according to current QThread documentation. I use the second way because some Qt examples used that method when I was first starting to use Qt. Only recently has the worker object method been proposed.
First, a “little" recommended background reading:
Threading without the headache (still wrong because it sub-classes QThread)
Threads, Events and QObject (big article covering all the details, also beta level as of this writing)
How To Really, Truly Use QThreads; The Full Explanation (Maya's blog with a simple and correct example)
Important Warning: there is one item that I would recommend being careful with in the last link. I see that a QString is being passed from a signal in the worker thread object to the slot in the main GUI thread. The QString object is implicitly shared and is dangerous to share between threads. Modify the QString in one or both threads after the signal is sent and you may corrupt memory and have a crash. According to the Qt documentation, if a copy of the QString is made before it is passed this might work without crashing, but from my previous experience it is better to be safe than sorry.
Qt_ThreadTest Application Structure
The structure of this example application is fairly convoluted because I needed to create a matrix of 9 combinations between the signal types and the receiving thread. In fact, it doesn’t even implement the worker thread the correct way because I sub-class QThread. Please see the last recommended reading item for a simple and correct example. I will provide a basic discussion of the program;
- Class ThreadWorkObject defines the thread work object and receives the signals. The signal receiving slot increments an integer value that is shown by the main GUI.
- Class ThreadObject defines a sub-classed QThread. It is a single thread that creates and runs all the worker objects.
- Class Qt_ThreadTest defines the main GUI window. It contains a static instance of a guiDisplayTimer and the testSignalThread.
- The testSignalThread constructor runs on the main GUI thread and sets almost everything up. It creates a QVector list of the 9 worker object examples which can be easily accessed under mutex protection.
- The testSignalThread run method does final setup of the worker objects created on the thread that will receive signals from a QTimer also created on the running thread.
The GUI thread of the Qt_ThreadTest application shows output strings of all the different combinations. As can be seen, when the thread worker object is created on the GUI thread, the signals are actually received on the GUI thread’s event loop. The other cases where the worker object is moved to the thread or created in the thread’s run method produce the designed condition, reception of the signal on the thread’s event loop.
The Wrong Way
In order for slots to receive signals correctly in a thread object, they must receive them from the thread’s event loop. The event loop is the thing that runs when you call the QThread::exec() function in your QThread::run() procedure. One would think you could just sub-class QThread, add some code, connect up a QTimer signal and let it go. Well, that doesn’t work. The reason is because the QObject that receives the signal must belong to the thread that has the event loop. Otherwise, the slot gets called on the event loop the QObject was created on, in this case, the GUI thread. This can be very dangerous because you think the slot is being called on the thread but instead it is called from the GUI thread. Access some thread shared data in the slot method and your application will go poof.
A Working but “Wrong" Way
The first solution I found to this problem was to move the GUI created QThread to the actual thread QObject after I created it using the QObject::moveToThread method. In this example program, I move both the threadTimer object and select thread worker objects to the testSignalThread object. This is not an ideal solution, but it works. No matter what object emits the signal, it is received by the thread worker object slot which belongs to the thread. It doesn’t matter where the signal comes from, the slot always executes on the thread’s event loop.
But this is considered “wrong” practice. However, I haven’t seen any issues after using it for many years.
The Right Way
I defer to Maya’s Programming Blog for an example of the proper way to operate threads (with the previous warning about passing QString between a signal and slot running on different threads). When I get some spare time, I plan to explore the problems with implicit shared objects that I previously saw. I know they caused problems for me in the past so I’ve either replaced them with non-shared equivalents or am very careful how I use them in threads.