Code This

std::cout <<me.ramble() <<std::endl;

Using QThread Without Subclassing

with 28 comments

The source code for this blog post can be found here. You do not need a Pro account to download the file, click No if you are prompted to upgrade.

Having been developing with C++ and Qt for around 6 years, and being a regular on #qt on freenode, a thought occurred to me. Hey, why not use that experience for a blog post? So, I thought I would start with one of the least understood classes in the Qt API, and that is QThread. This class is inherently difficult to use, because threading is difficult. Threading can turn a perfectly deterministic system into something non-deterministic. A threading issue is typically going to be much more difficult to diagnose and correct than other issues. Don’t get me wrong, threading is very useful, and serves a very important purpose (imagine using a web browser that didn’t use threads to fetch and render the content). But you should be familiar with both the positives and the negatives of the tools you are using to develop applications.

The first thing you need to know about using QThread is, a QThread object is not a thread. QThread is a class used for managing a thread; it is not itself a thread. The only method on QThread that runs on a separate thread is “run”. The second thing you need to know about using QThread is, as with all QObject subclasses, a QThread object has affinity to the thread in which it is instantiated. If you subclass QThread and add your own slots, you may be surprised to learn that these slots will actually run on the main thread. This also means you cannot use the QThread object as the parent of a QObject instance that has affinity to the thread (i.e., one created within “run”). And lastly, you need to know that only the main (GUI) thread can access GUI elements (QWidgets). The method of communicating between the thread and the GUI is signals and slots. If you don’t know, by default an object’s slots are run on the thread to which the object has affinity. This is done using the event loop.

Unfortunately, until Qt 4.4, QThread was an abstract class. The “run” method was pure virtual. And, even though this is no longer the case, the documentation still shows an example of using QThread by subclassing. While this is not wrong, or bad, it is not necessary. It also lends itself to using the class in ways it was not designed to be used. QThread is now able to be used without being subclassed. Attached is a small example application that creates a QTreeWidget and a button. Clicking the button will lead to the tree widget being populated with the contents of the user’s home direcrtory. Reading the contents of the file system is done on a separate thread. For this reason, you will see that you can still interact with the GUI while this is being done. There is a “sleep” call added, and tree widget items are expanded and collapsed while this is happening, so you are able see what is happening while the application runs.

So let’s jump into some code. This is the declaration of the FSReader class, the class that reads from the file system:

#ifndef FSREADER_H
#define FSREADER_H

#include <QObject>

class FSReader : public QObject
{
   Q_OBJECT

public:
   FSReader(QObject* parent = 0);
   ~FSReader();

public Q_SLOTS:
   void start();

Q_SIGNALS:
   void pathFound(const QString& path);
   void finished();
};

#endif // FSREADER_H

A pretty typical QObject subclass. As we will see later, the start slot will be executed when the thread begins to run, and the finished signal is emit’d when the object has completed its task. The pathFound signal is used to communicate with the GUI. The following is the implementation of the class:

#include "FSReader.h"
#include <QDirIterator>
#include <QDesktopServices>

#if defined(Q_OS_WIN32)
#include <windows.h>
#define SLEEP(msecs) Sleep(msecs)
#else
#define SLEEP(msecs) usleep(msecs * 1000)
#endif

FSReader::FSReader(QObject* parent)
   : QObject(parent)
{
}

FSReader::~FSReader()
{
}

void FSReader::start()
{
   QDirIterator it(QDesktopServices::storageLocation(QDesktopServices::HomeLocation), 
      QDirIterator::Subdirectories);
   while (it.hasNext()) {
      emit pathFound(it.next());
      SLEEP(1);
   }
   emit finished();
}

As you can see, the implementation of this class is quite simple. It only uses QDirIterator to iterate the files and directories in the user’s home directory, and emits a signal for each one found. As mentioned previously, the SLEEP call is simply to slow the program down a bit so you can see what is happening while it runs. It should not be used in a “real” application.

The last code snippet is also the most relevant, as it includes the use of QThread:

void Widget::onBtnClicked()
{
   QThread* thread = new QThread;
   FSReader* reader = new FSReader;

   reader->moveToThread(thread);

   connect(thread, SIGNAL(started()), reader, SLOT(start()));
   connect(reader, SIGNAL(pathFound(QString)), this, SLOT(onPathFound(QString)));
   connect(reader, SIGNAL(finished()), thread, SLOT(quit()));
   connect(reader, SIGNAL(finished()), reader, SLOT(deleteLater()));
   connect(reader, SIGNAL(finished()), thread, SLOT(deleteLater()));

   thread->start();
}

This is the button click handler of the Widget class. You can see here we instantiate both a QThread and FSReader instance, each without a parent. We then change the affinity of the “reader” to this new thread using the method “moveToThread”. This method can be used to change the thread affinity of any QObject.

Lets look at each connection that follows. The first is what starts the reader object after the thread has started. This method is run on the thread. The second connect links the reader object to the GUI. The “onPathFound” slot is run on the main thread. The next connect causes the thread’s event loop to exit once the reader has finished. The default implementation of QThread::run simply starts the thread’s event loop. The last 2 connects handle cleanup, since our objects were created without parents. The “deleteLater” method causes the objects to be deleted once there are no more pending events in the event loop. The reader is deleted on the thread, and the thread itself is actually deleted on the main thread. This “deleteLater” call works for the reader, even though the thread’s event loop would have already exited, because QThread does one final loop to handle all deferred deletetion events once its event loop has exited. QApplication does the same.

The rest of the Widget class, and the main, is included in the linked source, and is not particularly relevant to this post. That’s all you have to do to use QThread without subclassing it. Happy threading!

Written by Kris Wong

April 4, 2011 at 12:22 pm

28 Responses

Subscribe to comments with RSS.

  1. […] Originally posted here:  Using QThread Without Subclassing « Code This […]

  2. […] Read more here:  Using QThread Without Subclassing « Code This […]

  3. What happen if main application suddenly quit while thread is running? Are thread & reader pointer completely deleted?

    roz

    October 10, 2011 at 9:09 am

    • In general, all major operating systems will reclaim the memory used by your application after it exits. This scenario is still bad, though, as no cleanup routines are run and the application does not wait for the thread to finish what it is doing before exiting. If the thread were writing to a database, or a flat file, you might end up with data loss or file corruption, as just one example.

      Kris Wong

      October 11, 2011 at 9:10 am

  4. Wow thats great. I was having big problems when overriding QThread (http://ilearnstuff.blogspot.com/2012/08/when-qthread-isnt-thread.html). I didn’t realize “run” had a default implementation. This is definitely is the way to go!

    softwaredoug

    August 17, 2012 at 8:07 am

    • I am glad you found the post helpful.

      Kris Wong

      August 18, 2012 at 4:57 pm

  5. Hi, thank you for this great article. But i found that a connection can cause crashes in some cases:

    Instead of deleting the thread when reader finishes it is better to delete the thread when the THREAD finished/terminated by calling quit():

    so instead of:
    connect(reader, SIGNAL(finished()), thread, SLOT(deleteLater()));

    do:
    //this stops the thread on reader completion
    connect(reader, SIGNAL(finished()), thread, SLOT(quit()));
    //delete thread only when thread has really finished
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    conect(thread, SIGNAL(terminated(), thread, SLOT(deleteLater()));

    deleting a thread while it has not finished for sure, causes crashes or the message that the thread was destroyed while still running…

    Hope this helps, maybe you can add this to your article because I spend a lot of time finding this out.

    Thank you!

    Юрій Николаев

    October 5, 2012 at 4:05 am

    • You make a great point. Thank you for your comment!

      Kris Wong

      October 5, 2012 at 8:07 am

    • Why do you connect both finished() and terminated() to deleteLater() on the thread? Is finished() not enough?

      Mark Trolley

      October 22, 2012 at 9:08 am

      • If the thread is stopped by calling terminate(), finished is never emitted so the thread object is not deleted which leads to memory leaks… so if the thread terminates regulary (job done) finish is emitted but when calling terminate() (for ex. from destructor) only terminated() signal is emitted

        ;)

        Юрій Николаев

        October 22, 2012 at 12:19 pm

      • Got it, thank you!

        Mark Trolley

        October 22, 2012 at 12:23 pm

  6. so Greate…..include the replys

    hohos

    October 24, 2012 at 7:55 am

  7. Hi, How can you use QThread::sleep() in FSReader which is not a subclass of QThread class. And indeed, the code you provided does not compile because of the SLEEP macro on my win7 32bits PC? Did I miss something here? If I am a right, loosing the sleep() method when not subclassing QThread seems to be a major pitfall. Is there a workaround ?
    Gilles

    Gilles

    November 9, 2012 at 6:01 am

  8. I will take a look. Thanks a lot. I have never used platform dependent API in Qt so far.
    I am new on C++ and Qt , but I guess QThread::sleep() is mostly use in test and training programs and less on real programs. Nevertheless, as far as the second part of my question is concerned, is there a platform independent (pure portable Qt without precompiled statements) workaround to replace the QThread::sleep() function? If not, and if you allow to level little criticism, that this is a little drawback for this QThread method, which does not tip the balance on the other side tough. I do appreciate the way all slots and methods of the worker object are executed in the new thread (when invoked though the thread loop of course, not directly).
    Gilles, Paris

    Gilles

    November 10, 2012 at 6:06 am

    • Below is the pure Qt solution I just found which consists in making the protected static method QThread()::sleep() public :
      class MThread : public QThread {
      Q_OBJECT
      public:
      MThread(QObject *parent) : QThread(parent) {}
      static void Sleep(unsigned long secs) { sleep(secs);}
      };

      Then in the worker code, sleep like that :
      static_cast(QThread::currentThread())->Sleep(2);

      The worker is created using QThread not MThread:
      worker1 = new Worker;
      thread1 = new QThread;
      thread1->start();
      worker1->moveToThread(thread1);

      It works in my simple test example, but do you see any undesirable side effects for the real world? Or say it another way , why is QThread()::sleep() a protected method? There should be a good reason and I kind of worry to use a modified Qt feature like that.

      Gilles, Paris

      Gilles

      November 10, 2012 at 8:38 am

      • =>correction. Sleep like that : static_cast(QThread::currentThread())->Sleep(2);

        Gilles

        November 10, 2012 at 8:41 am

      • the forum editor does not display well my cast but i guess you all know what i mean in the context. I static cast to MThread* before calling Sleep()

        Gilles

        November 10, 2012 at 8:47 am

      • Sleeping a thread is not something you should do in a “real” application. It is used in the example for illustrative purposes. In my opinion, QThread should not have a sleep method at all.

        Kris Wong

        November 10, 2012 at 12:35 pm

  9. Thanks Kris . Your article really helped me to understand this method to create a thread.
    I go on the discussion on QThread::sleep() at http://qt-project.org/forums/viewthread/21856/

    Gilles

    November 12, 2012 at 8:07 am

  10. This way FSReader constructor laying in main thread, and other methods in separate thread. What about data members, data initializations etc.?
    I think the better way is subclass QThread class and then in run() method instantiate FSReader class, so the whole FSReader object would be in separate thread.

    Boban Jovanović

    November 15, 2012 at 2:29 pm

  11. Actually, you can fully use QThread by subclassing it.

    After a few hours of poking around, a came up with this solution: http://engineer-dan.tumblr.com/post/36274332373/subclassing-qthread

    Basically what I did was subclassed QThread, then called moveToThread(this) in the constructor.

    engineerdan

    November 22, 2012 at 3:52 am

  12. I have stumbled upon a problem with this code. When connecting the start signal to the worker function the function can be called immediately when the signal is emitted. Because in the function (at least for windows) the terminationEnabled flag is disabled during thread start and reenabled after the start signal this can mean, if the worker function does not return the terminationEnabled flag will never be reenabled.

    Because of this it is not possible to use terminate() to abort the thread anymore.
    In the Note to the documentation http://qt-project.org/doc/qt-4.8/qthread.html is another way described for the start, that does not have that problem. Instead of connecting started to doWork, just use:
    thread->start();
    QMetaObject::invokeMethod(worker, “doWork”, Qt::QueuedConnection);

    Kraven

    January 2, 2013 at 9:40 am

  13. This is great ..I’m trying it

    Source link is broken ..Please fix.

    sss

    January 24, 2013 at 11:16 pm

  14. Great article….

    I have one question regarding this. Can the process() function of Worker be made parameterized? If yes, how can I pass some parameters to that function when the thread starts?

    Parag Shravagi

    June 11, 2013 at 4:03 am

  15. hi. as “sss” at top said; would you please fix the source code Link?i need that

    rosenth

    August 15, 2013 at 10:16 am

  16. Hi. I would like to try your example. The download link is broken.Can’t download the example. Can you please fix ? Thank you.

    Fred Bartholomai

    March 25, 2014 at 9:07 am

  17. Hi, yip, the link is broken. Please try and fix it? The example will aid my studied in Qt. Thanks.

    Niel

    April 21, 2014 at 9:34 pm


Leave a reply to engineerdan Cancel reply