The great QThread Mess

Stone Age Detail 1 (Wiktor Michajlowitsch Wassnezow, via Wikipedia commons, public domain)

Stone Age Detail 1 (Wiktor Michajlowitsch Wassnezow, via Wikipedia commons, public domain)

When writing applications with a graphical user interface, it is important to keep the user interface snappy even while the application performs heavy duty background tasks like file processing or downloading huge files from a network. The best way to accomplish that is to use multithreading. Qt4 provides a convenient way via its QThread class.

Though I already have written some multithreaded application using QThreads, I did some research to get a better clue about the best practise to use them. Since Qt4, signals and slots are thread save, so those are the perfect candidates for inter thread communication as well as for data exchange across the threads. Actually, each thread can run its own event loop after exec() being called, and Qt hides the details about thread safeness (maybe mutexes or semaphores) behind the scenes. But anyway, the concept is not really well understood.

The documentation of QThread prior to Qt 4.4 is like:

Instead of starting in main(), QThreads begin executing in run(). To create your own threads, subclass QThread and reimplement run().

The documentation of QThread since Qt 4.4 is like:

Instead of starting in main(), QThreads begin executing in run(). By default, run() starts the event loop by calling exec() (see below). To create your own threads, subclass QThread and reimplement run().

After run() is reimplemented, the start() method is used to begin the thread execution. The problem is to find good examples how to write proper code. Lets have a closer look at some examples.

When you write a run() method like the following, the event loop will be started after the work is done, which means the thread will neither send nor respond to signals during the operation. This way it is impossible to update the user interface, e.g. to show a progress bar while downloading some huge files or to provide a cancel button while compressing files or folders. But as exec() does not return, both methods cannot be swapped:

protected:
void run() {
doHeavyWork();
exec();
}

Dave Smith thus shows the following approach to solve this problem. doTheWork() is not called in the run method directly. Instead it gets invoked by a signal sent by a QTimer:

run()
{
QTimer::singleShot(0, this, SLOT(doTheWork()));
exec();
}

Frankly, I immediately felt uncomfortable with this approach. In single threaded applications, QTimer events have been used to update the user interface in certain intervals, while a heavy operation forced the event loop being processed every now and then. Using a QTimer in a thread thus appears like an Anachronism.

Another thing you’ll find quite often is the moveToThread() method, which is part of the QObject base class. I read examples where people recommended to invoke it in the constructor of the class inherited by QThread:

this->moveToThread(this);

I’m not convinced that Harold Hunt hit the nail on its head:


MyThread::MyThread {
this->start();
QObject::moveToThread(this);
}

Bradley T. Hughes tries to set things right in his posting »You’re doing it wrong…«. He also points to another of his postings, Threading without the headache, which now is four years old and still valid. He shares example code which is clear and simple:


Producer producer;
Consumer consumer;
producer.connect(&consumer, SIGNAL(consumed()), SLOT(produce()));
consumer.connect(&producer, SIGNAL(produced(QByteArray *)), SLOT(consume(QByteArray *)));

QThread producerThread;
producer.moveToThread(&producerThread);
QThread consumerThread;
consumer.moveToThread(&consumerThread);

producerThread.start();
consumerThread.start();

I still did not get my own code written, but I hope the above information helps some people to avoid common mistakes. The point is, that you neither need to subclass QThread nor to move a thread class to itself. Instead, instantiate a QThread object directly. Create classes to encapsulate your data and algorithms and move those classes to the threads already instantiated. Use signals and slots to glue everything together.

Back to work :) .

3 thoughts on “The great QThread Mess

  1. ce Post author

    Yes, I tried the examples (like the producer-consumer example). I’m currently struggling with connecting signals and slots in one of my classes, but this issue seems to be unrelated to QThreads.

Comments are closed.