QtThreads are not scary! – part 1 – How not to write It


From my experience QThread class can be somewhat confusing to use. Mostly due to how they work, not because they are hard, so here is a short explanation what and how to use threads in qT.
In the first place why to use threads? Threads are useful for heavy duty or computation expensive tasks that cause GUI to freeze. In example video compression takes a long time and is very CPU intensive. If that task is run on the same thread as you main GUI, in main application thread, program don’t receive events and operating system mark it as non responsive. Of course, if application is well written, after video compression GUI “unfreeze” and program works fine after that. This behaviour is not expected, because user don’t rally know if program works correctly or not.
There is also another reason why to use threads. Nowadays modern CPU consist of so called cores. They are logical units, that perform task in parallel. That means that one core perform one task and other one another. This behaviour is similar to multi CPU platforms, that consists of more then one physical CPU unit.
In past increase in CPU performance was done via increase of clock rate of the chip. The ratio performance/clock rate was similar to the linear increase, but somewhere around 2GHz, and more, ratio was less significant. Also cost of producing high clock rated CPU increase, so engineers decide to place additional unit inside single CPU circuity instead of increasing it’s rate. That way overall performance increase much more then simple clock rate increase.
That gave user more power and programmer more headache. Why is that? Because not all operation are, from computation point of view, highly parallel scalable. In the end performance increase very from few % to few hundreds %. In example tasks like ray trace or global illumination are highly scalable and executing them on multi CPU platform yields significant performance increase.
Of course there are some other problems, like dividing task (threads) to individual core. Ideal behaviour would be to assign each thread to each free core (take for example iCore7 with 6 cores and Hyper-Threading that allows to run 12 thread in parallel) with thread without blocking I/O operation. This is only a brief overview and there is much more to threading like mutex, semaphores or TBB (Intel Threading Building Blocks) and so one.
But… lets back to the subject and focus on thread usage in Qt applications. Qt offers QThread class that allows programmer to utilize usage of the threads. Bellow is an simple example how to use threads.

In actual implementation You would want to use full potential of target (user) machine, but the problem with that is that, you don’t know how many threads target system supports. And why You would want to know that? Because, as stated before, on one core CPU there would be no speed up by using threads, only GUI won’t freeze. So to learn haw many thread (or actually cores) target system support use convenient function int QThread::idealThreadCount (); It returns core count (or ideal thread count) as integer number, i.e. on my AMD Athlon 64 2.2GHz San Diego function output’s 1, with is correct, because I use 1core CPU, so running more then 1 thread don’t yield performance increase but actually drop in performance due to information exchange between threads (tested on heavy duty operation in Zbrush and 3DSmax). On other hand, on my Intel Core 2 Duo T7100 output says 2, and that’s also correct, because I got two cores. So knowing how many cores user system has can gave use enough information to optimize implementation to fully utilize user system.

Correction!
First of all brief explanation about article itself.
I written it a quite some time ago, and it come to my attention, due to few post [2,3,4], and some work that I done with threaded application, that information that I provided in this article are actually WRONG. So to not confuse readers I changed title from: “QtThreads are not scary!” to “QtThreads are not scary! – part 1 – How not to write It”. The mistake was mainly due to manual itself, that don’t make it clear.
So basically what’s wrong with this code is that work is done in QThread sub-classed class. The correct way of doing it is to create class,  i.e. subclass QObject HeavyDutyStuff and then use QThread as a wrapper for that object. Something along these lines:
HeavyDutyStuff ->moveToThread(QThread);

I leave this article as it is, so You will know how NOT to write thread, and rewrite this example the way it should be in “QtThreads are not scary! – part 2 – Correct way”. Well probably I should also change title to “QThreads are indeed scary” :).

My OLD WRONG WAY!

So lets focus on QThread itself.
Fist thing to do is to subclass QThread. You can do that in two ways, first typing by hand, create two files, i.e. “mythread.h” and “mythread.cpp”:

"mythread.h"
class myThread : public QThread
{
Q_OBJECT //macro for SIGNAL/SLOT mechanism

public:
explicit myThread(QObject *parent = 0);

signals:
void pbValue(int, QImage );

public slots:
void heavyDutyJob();

protected:
void run();

};

and:

"mythread.cpp"
myThread::myThread(QObject *parent) :
QThread(parent)
{
this->moveToThread(this);
}

void myThread::run()
{
QTimer::singleShot( 0, this, SLOT(heavyDutyJob()) );
this->exec();
}

void myThread::heavyDutyJob()
{
//do stuf here
this->exit();
}

or using QtCreator. In creator select File->New File or Project… -> C++ class,  and in displayed window, type into fields:

  • Class name: i.e. myThread
  • Base Class: QThread
  • Type information: Inherits QObject

after that Next->Add to project.
Either way You will end up with something similar to the code shown above.
First step is done, next create myThread object in i.e. MainWindow class, assuming that’s “Qt4 GUI Application” project, and place it on the stack.

#include "mythread.h"
//...
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
//...
private:
myThread *thread;
//...
};

and inside that class, depending on the needs, You can place fallowing code in i.e. on push button event:

void MainWindow::on_pushButton_clicked()
{
thread = new myThread();
thread->tmp = pix_qtLogo.toImage(); //non GUI thread dont like QPixmap
connect( thread, SIGNAL(started()), this, SLOT(threadStartJob()));
connect( thread, SIGNAL(finished()), this, SLOT(threadEndJob()));
//my custom update signal, not present in above code, but present in example code, link bellow
connect( thread, SIGNAL(pbValue(int, QImage)), this, SLOT(updatepb(int, QImage)));
thread->start();
}

at this point everything should compile fine, and thread would execute, do the heavy duty job and finish it’s operation.

Screen presenting example application:

thTest_mainWIndow

thTest mainWIndow

As you can see I connected some slot to signal’s from thread. There are two inherited signals: started() and finished() and one custom, made by myself, SIGNAL( pbValue( int, QImage) ). First two, as name suggest, are executed at the beginning of thread execution ( started() ) and when thread end’s it’s job ( finished() ). My signal simply inform progress bar, placed on main form, about thread operation progress.

thTest_code_run_in_thread

thTest code run in separate thread

Also I convert QPixmap into Qimage. In my example, code posted below, I use image and convert that image, inside the thread, into grey scale version. I do this conversion, because QPixmap can’t be used in non GUI thread, probably due to memory allocation on screen for itself (that said, it works fine under windows because it’s not use GDI, but not in Linux, see Qt doc for more info). This is good example how to exchange data between thread and main class.
And finally, using thread->start(), thread execution starts.

Using same code in main GUI thread (application thread) results in GUI freeze. On Windows, as far as I know, program is “marked” as non responsive when application don’t process user inputs (events), on Linux only after particular amount of time (probably depending on the distribution).

thTest_code_run_in_GUI_Thread_freezGUI

thTest code run in GUI Thread, freeze GUI

Here is a work flow how code inside thread is executed:

thread = new myThread();

call a constructor

myThread::myThread(QObject *parent) : QThread(parent)
{
this->moveToThread(this);
}

thread is on the stack, so you can start it:

thread->start();

at this point protected function run() is called automatically, it’s starting point for the thread:

void myThread::run()
{
QTimer::singleShot( 0, this, SLOT(heavyDutyJob()) );
this->exec();
}

inside run() You want to start exec()ution of the thread. By calling exec() thread enters it’s event loop. In above example slot heavyDutyJob() is called.

void myThread::heavyDutyJob()
{
//code here
this->exit();
}

This slot runs until either exit() ( tells thread to stop with return code, non 0 values means error ) or quit() ( exit code with return code equal to 0, don’t check if there was some kind of error ) is called.
Exec() is actually a int myThread::exec(), and serves the same purpose as int main(). To put it simply, it’s main function for the thread. If Your thread don’t exit()/quit(), then when You delete that thread, it will yields an error like “QThread: Destroyed while thread is still running” or similar. Depending on the moment when You delete that thread few things can happen. If delete thread is called when job is finished only that error will “pop up” but the program will work fine. If You delete thread before code actually finish the job obviously application will probably gave an critical error and hang/close.

This article is only a introduction to threads and covers only basics. There is also something called mutex (mutual exclusion), and semaphores. In nutshell these techniques allows to control access to non shareable data. Probably future articles will cover these subjects.

Here is source code for this example: thTest_src

Reference:
[1] Series of free lectures about multicore programming by MIT OpenCourseWare, to be more precise MIT 6.189 Multicore Programming Primer, IAP 2007
[2] http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong/
[3] http://labs.trolltech.com/blogs/2006/12/04/threading-without-the-headache/
[4] http://blog.exys.org/entries/2010/QThread_affinity.html

, , , , ,

Comments are closed.