This chapter reveals why “just run it in a separate thread” is almost never the solution. Here you’ll discover where exactly freezes originate, learn the secret of safe data exchange between threads, and uncover how professional Qt developers keep the GUI responsive even under load — without architecture chaos and without nighttime debugging sessions.
We’ll examine 2 practical threading strategies (inheriting from QThread and the recommended approach via moveToThread()), “proper” asynchrony with QProcess, and 4 synchronization tools (QMutex/QMutexLocker, QSemaphore, QWaitCondition, QReadWriteLock) and typical scenarios where they save — or kill performance.
And yes: at the end, it will become clear how to move to the next level with QtConcurrent, QFuture, then() chains, and QPromise, to assemble asynchrony like a constructor, not a minefield.
Skipping this chapter is easy — but then you’ll pay with freezes, deadlocks, and strange “sometimes it works”.
This chapter includes ready-to-use code examples.
Chapter Self-Check
Why is the moveToThread() method the recommended approach in Qt6 instead of inheriting from QThread?Answer
Correct answer: This approach provides cleaner separation between the thread and the task being executed, allows using signals and slots for thread management, and simplifies implementation of operation cancellation mechanisms.
Why can’t you create QWidget objects and call their methods in non-main threads?Answer
Correct answer: Qt GUI classes are not thread-safe and can only work in the main application thread; attempting to work with widgets in other threads will lead to unpredictable behavior and crashes.
What’s the fundamental difference between postEvent() and sendEvent() when working with threads?Answer
Correct answer: The postEvent() method is thread-safe and adds an event to the queue for asynchronous processing, while sendEvent() is not thread-safe and causes synchronous event processing.
What happens inside Qt when a signal from one thread is connected to a slot in another thread?Answer
Correct answer: In AutoConnection mode, Qt automatically converts the signal call into an event that’s placed in the target thread’s event queue for safe inter-thread communication.
Why is the QThread class not itself a thread, and how does this affect slot execution?Answer
Correct answer: QThread is a thread management mechanism, not the thread itself; QThread object slots will execute in the thread where the object was created (usually main), not in the managed thread.
What is a deadlock and how does it occur?Answer
Correct answer: Deadlock occurs when two or more threads block each other: each holds a resource and waits for release of a resource held by another thread, leading to infinite waiting.
How does a semaphore differ from a mutex in principle?Answer
Correct answer: A semaphore generalizes a mutex, allowing simultaneous access by a certain number of threads (counter), while a mutex allows access to only one thread.
Why can applying locks (mutexes) reduce application performance?Answer
Correct answer: Each lock/unlock operation takes time and can lead to threads idling while waiting for resource release, which reduces parallelism and overall efficiency of a multi-threaded application.
In which cases should you use QProcess instead of creating threads?Answer
Correct answer: QProcess is suitable for launching external programs or console commands, especially when you need to use ready-made functionality without GUI or perform a short-term operation independently of the main process.
When should you use QReadWriteLock instead of regular QMutex?Answer
Correct answer: QReadWriteLock is efficient when multiple threads can safely read data simultaneously, but writing must be exclusive; this improves performance when read operations predominate.
What problem does using QMutexLocker solve compared to direct lock()/unlock() calls?Answer
Correct answer: QMutexLocker automatically unlocks the mutex when going out of scope (in the destructor), guaranteeing resource release even when exceptions occur or early function exit.
What’s the advantage of the QtConcurrent framework over direct QThread usage?Answer
Correct answer: QtConcurrent creates a high-level abstraction, automatically managing thread creation, synchronization, and task distribution, significantly simplifying code and accelerating development.
How to avoid deadlock when working with multiple resources in threads?Answer
Correct answer: You can establish a single resource capture order for all threads, use tryLock() with release on failure, or apply deadlock detection and resolution algorithms.
What is the QPromise class in Qt6 for and what problems does it solve?Answer
Correct answer: QPromise provides explicit management of asynchronous tasks, allowing adding intermediate results, tracking execution progress, correctly completing tasks, and handling exceptions.
Why is blocking the main application thread critical for GUI applications?Answer
Correct answer: Blocking the main thread suspends the event processing loop, causing the user interface to stop responding to user actions, creating the impression of a “frozen” application.
Practice Assignments
Easy Level
Countdown Timer in Separate Thread
Create an application with a QLCDNumber widget and a “Start” button. When the button is pressed, a countdown from 10 to 0 should start in a separate thread with a 1-second interval. Use the moveToThread() approach to move the worker object to the thread.
Hints: Create a Worker class with QTimer, use the valueChanged(int) signal to pass values. Connect the QThread::started() signal to the timer start slot. Don’t forget to call thread.quit() and thread.wait() when finished.
Medium Level
Multi-threaded String List Processing
Develop an application that takes a list of 100 strings and processes them in multiple threads using QtConcurrent::mapped(). Each string should be converted to uppercase and supplemented with a sequence number. Display processing progress in QProgressBar and results in QTextEdit.
Hints: Use QFutureWatcher to track execution progress. Connect progressRangeChanged() and progressValueChanged() signals to QProgressBar slots. After completion, use future.results() to get all results.
Hard Level
Thread-Safe Priority Task Queue
Implement a thread-safe task processing system: TaskQueue class with priorities (high, medium, low), three worker threads processing tasks from the queue, and GUI for adding tasks and displaying their status. Use QMutex, QWaitCondition, and signals for synchronization. High-priority tasks should be processed first.
Hints: Use QMutex to protect queue access, QWaitCondition to wake waiting threads. Store tasks in QQueue or QPriorityQueue. Worker threads should inherit from QThread with run() override. Implement correct thread termination via a flag and wakeAll().
💬 Join the Discussion!
Got a handle on multi-threaded programming nuances? Encountered a deadlock or don’t know when to use QMutex versus QtConcurrent?
Share your experience solving synchronization problems, discuss best practices for working with threads, or help other readers avoid common mistakes!