There is ongoing confusion, and some contention, over the right way to use the QThread class. Part of the confusion comes from the uniqueness of QThread, which is significantly different from other threading classes. There are two valid patterns of QThread use, and each pattern is best suited for different use cases. Errors will arise when mixing these two patterns, or applying them inappropriately.
This presentation will look at different patterns of threading in general, and examine in detail the two usage patterns for QThread. This talk will also cover how QThread works inside, how to communicate between threads, provide examples, and point out the pitfalls to avoid.
Less Is More: Utilizing Ballerina to Architect a Cloud Data Platform
QThreads: Are You Using Them Wrong?
1. QThread
Are you doing it wrong?
Roland Krause
Integrated Computer Solutions
based on material from
David Johnson
2. What are we talking about!
● Controversy and confusion
● Patterns of threading
● How QThread works
● Pitfalls to avoid
● Examples
3. Controversy
“You are doing it wrong!”
- Brad Hughes 2010
“You were not doing so wrong”
- Olivier Goffart 2013
“Have you guys figured it out yet?”
- Developer 2014
4. What is this all about?
● A casual reading of some blog posts leads one
to imagine there is a “right” way and a “wrong”
way to use QThread
https://blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html
https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-
qthreads-the-full-explanation/
https://fabienpn.wordpress.com/2013/05/01/qt-thread-simple-and-stable-with-
sources/
http://stackoverflow.com/questions/4093159/what-is-the-correct-way-to-
implement-a-qthread-example-please
● There are two valid patterns of QThread use
● Both are suitable for different use cases
5. The Reason for the Confusion
● Only one pattern of QThread use prior to 4.4
○ Must subclass QThread and implement run()
● New pattern of QThread use since 4.4
○ Default implementation for run()
○ Default implementation simply calls exec()
● Documentation was not fully updated until Qt 5
● Problems arise when mixing the two patterns
6. Four Threading Patterns
● Runnables - Pass runnable object to thread
○ QThreadPool, Java
● Callables - Pass callback or functor to thread
○ Qt::Concurrent, std::thread, Boost::thread
● Subclassing - Subclass and implement run()
○ QThread, Java
● Event Driven - Events handled in associated
thread
○ QThread
Notice that QThread has two patterns of use
8. Runnable Pattern
QThreadPool / QRunnable
● Create a QRunnable subclass
● Pass runnable object to QThreadPool::start()
● Very simple, but very low level
● Good for “fire and forget” style tasks
● Good for CPU-bound tasks
9. Example Runnable
class HelloWorldTask : public QRunnable
{
void run()
{
qDebug() << "Hello world from thread"
<< QThread::currentThread();
}
}
HelloWorldTask *hello = new HelloWorldTask();
// QThreadPool takes ownership
// and deletes 'hello' automatically
QThreadPool::globalInstance()->start(hello);
Use it for: Asynchronous Tasks with Trivial
Parallelism, i.e. no communication
10. Callable Pattern
QtConcurrent
● Functional programming style APIs for parallel
list processing
○ Concurrent Map-Reduce
○ Operates concurrently over collections and ranges
○ Supports several STL-compatible container and iterator
types
○ Works best with Qt containers that have random-
access iterators, such as QList or QVector.
○ Can use: Function pointers, functors, std::bind
● Actual threads are managed by QThreadPool
● Good for parallel tasks
11. Callable Pattern
● QFuture represents the result of an
asynchronous computation.
● QFutureWatcher allows monitoring a QFuture
using signals-and-slots.
Use it for: Problems with Trivial Parallelism, e.g.
indexing of large data sets
12. Subclassing Pattern
● Subclass QThread
● Re-implement the run() [protected] method
○ default run() implementation calls exec()
● Call start(), start() calls run()
● Intended for blocking tasks that don’t need
interaction with the main thread
● If you do not really need an event loop in the
thread, you could subclass
Example: Encoding data
13. Event Driven Pattern
● Create a Worker [Q]Object
○ Implement methods that do the (long running, blocking) work, e.g.
doWork()
● Move this worker object to the thread managed by the
QThread object
○ Creates thread affinity
○ Connect QThread::started to Worker::doWork
● Call QThread::start()
○ emits started() when the actual thread has actually started
○ Worker's slots will be executed in its thread
● Use this for: Threads that need to interact with other
threads through events and/or signals and slots
14. The Confusion Sets In
There are two patterns of QThread use.
Mixing subclassing and event driven patterns is
an easy pitfall to stumble into
Although the Qt documentation for QThread
never mentions the QObject::moveToThread()
method until the “event pattern” was introduced in
Qt-4.8
15. Where the Serious Trouble Starts
Is when doing things like this:
MyThread::MyThread() : QThread()
{
moveToThread(this);
start();
}
void MyThread::run()
{
connect(Manager, &Manager::newData,
this, &MyThread::processData);
// ...
}
16. QThread and QObjects
● Each thread can have its own event loop
● A QObject lives in the thread in which it is created
○ Events to that object are dispatched by that thread's event loop.
● QObject::moveToThread() changes thread affinity
○ For object and its children
○ The object cannot be moved if it has a parent!
● Unsafe to call delete on a QObject from a different thread
○ As is accessing the object in other ways. Protect with mutexes, etc..
○ Use QObject::deleteLater()
17. A QThread is not a Thread - What?
Yes you heard that right:
● QThread is not a Thread, QThread manages a Thread
○ Threads are modes of code execution, they are not objects.
○ This is true for all common frameworks (Posix, Win32, etc.)
● With few exceptions, QThread methods do not run in the
Thread of execution.
“All of the functions in QThread were written and intended to
be called from the creating thread, not the thread that
QThread starts”
- Bradley T. Hughes
18. The Basics of QThread
QThread manages one thread of execution
● The Thread itself is defined by run()
○ Note that run() is a protected method
● Calling start() will create and start the thread
● QThread inherits from QObject
○ Supports properties, events, signals and slots
● Several threading primitive classes enable
thread execution control
○ QMutex, QSemaphore, QWaitCondition, etc..
19. QThread Event Loop
Default implementation of run()
void QThread::run()
{
(void) exec();
}
● The only* method that runs in the thread
context
● Starts an event loop running in the thread
● The event loop is key to thread communication
*Qt-5.5 introduces loopLevel()
20. QThreads and Events
● Can use custom QEvents to communicate
between QObjects in different threads
● Event loop of the receiver must be running
● Event loop of the sender is optional
● Bad form to use shared data in the event
object
void MainWindow::onActivity()
{
//...
ActivityEvent *event = new ActivityEvent(a, b);
QApplication:postEvent(worker, event);
}
21. Signal Slot Connections and Threads
● Qt::DirectConnection
○ Slots are called directly
○ Synchronous behavior
● Qt::QueuedConnection
○ Signals are serialized to an event
○ Asynchronous behavior
● Qt::AutoConnection (default)
○ If both objects have same thread affinity: Direct
○ If objects have different thread affinity: Queued
● Blocking Queued Connection
● Unique Connection
22. Cross Thread Signals and Slots
● Default connection between objects of different
thread affinity is Qt::QueuedConnection
● 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 just wrong. It
implies you want to send cross-thread signals to
yourself.
23. Cross Thread Signals and Slots
● Cross thread signals are really events
○ The receiver needs a running event loop
○ The sender does NOT need an event loop
○ Signals are placed in the event queue
● All threads can emit signals regardless of
pattern
○ Only threads with running event loops should have in-
thread slots
24. Back to Threading Patterns
Let's look closer at the two threading patterns for
QThread...
25. The Subclassing Pattern
Use when task...
● ... doesn't need an event loop
● ... doesn't have slots to be called
● … can be defined within run()
Most classic threading problems
Standalone independent tasks
26. Subclassing Example
class WorkProducer : public QThread
{
public:
WorkProducer(int items=0);
protected:
virtual void run();
WorkItem produceWork();
...
};
class WorkConsumer : public QThread
{
public:
WorkConsumer(int id);
protected:
virtual void run();
void consumeWork(const WorkItem &item);
...
};
27. Subclassing Example, cont.
void WorkProducer::run()
{
for (int n = 0; n < numitems; n++) {
WorkItem item = produceWork();
QMutexLocker locker(&queuelock);
workqueue.enqueue(item);
if (worknumwaiting > 0) {
workcondition.wakeOne();
worknumwaiting--;
}
}
}
28. Subclassing Example, cont.
void WorkConsumer::run()
{
while (!workisdone) {
QMutexLocker locker(&queuelock);
if (workqueue.isEmpty()) {
worknumwaiting++;
workcondition.wait(locker.mutex());
} else {
WorkItem item = workqueue.dequeue();
locker.unlock();
consumeWork(item);
// send an inter thread signal to the GUI thread
emit newValue(QString("Thread %1 Value is %2")
.arg(threadid)
.arg(item.getValue()));
}
}
}
29. Subclassing Example, cont.
WorkQueue::~WorkQueue()
{
// tell all the consumer threads to exit
queuelock.lock();
workisdone = true;
workcondition.wakeAll();
queuelock.unlock();
// wait for each thread to complete
producer->wait();
producer->deleteLater();
foreach (WorkConsumer *consumer, consumerlist) {
consumer->wait();
consumer->deleteLater();
}
}
30. Pitfalls of Subclassing
● Providing slots for the subclass
○ Since thread affinity has not changed, and there is no
event loop, slots will be executed in the caller's thread.
● Calling moveToThread(this)
○ Frequently used ''solution'' to the pitfall above
○ Threads must never have affinity with themselves
31. The Event Driven Pattern
Use when task...
● … is naturally event driven
● … needs to receive communications
● … does not have a single entry point
Inter-dependent tasks
“Manager” tasks
32. Event Driven Example
class Worker : public QObject
{
Q_OBJECT
public:
Worker(QObject *parent = 0);
public slots:
void process(const QString &filename);
signals:
void status(int);
...
};
33. Event Driven Example, cont.
void Worker::process(const QString &data)
{
QFile file(filename);
if (!file.open(QIODevice::ReadOnly) {
emit status(PROCESS_ERROR);
return;
}
int percent = 0;
while (!file.atEnd()) {
QByteArray line = file.readLine();
// process line
...
emit status(percent);
}
}
35. Event Driven Example, cont.
void Window::closeEvent(QCloseEvent*)
{
...
if (mWorker) {
QThread *thread = mWorker->thread();
if (thread->isRunning()) {
thread->quit();
thread->wait(250);
}
}
}
36. Drawbacks of Event Driven Pattern
● Does your task really need to be event driven?
● Event starvation
○ Compute intensive tasks take too long to return to
event loop
○ Checkout: QThread::isInterruptionRequested()
● Fake event loop anti-pattern
○ An entry point never exits to the event loop
void Worker::start()
{
while (true) { ... }
}
37. Event Driven Plus Subclassing
● Choice of pattern is not an either/or decision
MyThread::run()
{
... initialize objects
... setup workers
... handshaking protocols
exec();
... cleanup thread resources
... wait on child threads
}
38. What About QThreadPool?
QThreadPool is ideal for managing “fire and
forget” tasks!
However...
● Hard to communicate with the runnables
● Requires either threading primitives...
● Or requires multiple inheritance from QObject
39. What about QtConcurrent?
Higher level threading API However...
● Mostly limited to parallel computations
● Recommend to have knowledge of parallel
algorithms
● Recommended to have knowledge of
advanced C++ features
40. QThread!
Remains the workhorse of Qt threading
● General purpose threading solution
● Solid, Tried and True, Cross-platform
Threading API
● Compare to Boost - Ugh!
● QObject wholesome goodness
41. Now You Are Doing it Right!
There's no one right way to use QThread
But there are two good ways to use QThread
Subclassing Pattern
Event Driven Pattern
42. There once was a coder from Lodz
Who thought that his wife yelled too much
He moved to his own QThread
And everything that she said
He ignored - Signal-Slot mismatch!
Thanks David!