Skip to content

The Eight Rules of Multithreaded Qt The biggest dos and don'ts for multi-threading in Qt

While the concept of multithreading may be straightforward, code with threads is responsible for some wicked bugs, which can be nearly impossible to reproduce or track down. This makes writing bullet-proof code using threads a tall order. Let’s look a little deeper into why that is.

First, you need better than average knowledge about the internals of your frameworks, language, and compiler to know how to avoid threading trouble-spots. Second, you need to know about synchronization primitives and appropriate design patterns so you can create multi-threaded code that operates correctly under all conditions. And finally you need to understand how to use debugging tools with multiple threads to be able to find those tricky to reproduce issues that are inherent in multithreading bugs.

When it comes to Qt and multithreading, it’s especially true that you need to know your framework and design patterns. Qt gives you the power to make amazing multithreaded apps – as well as shoot your foot off. We’ve honed our multi-threading expertise over the years by finding and fixing threading bugs in both the Qt framework and Qt client code. Here’s a short list of our top rules for avoiding the most common pitfalls to have your Qt apps run right the first time:

1. Never call QThread::sleep()

Although there’s an API to allow your thread to sleep, that is QThread::sleep() – if you’re calling it you should really consider an event-driven design. By changing “threads that sleep” into “threads that wait for events” (or, better all, no threads at all), you’ll save a huge amount of system resources that are otherwise wasted by idle threads. QThread::sleep() is also bad for timing since the amount of time it takes before control is returned is poorly constrained. Sleeping threads can also theoretically cause problems during application termination; foreground threads can prevent the application from terminating until they awake, while background threads may never reawaken, preventing clean finalization.

2. Never do GUI operations off the main thread

Qt’s GUI operations are not thread safe, so non-main threads cannot safely perform any GUI operation. That means no widgets, QtQuick, QPixmap, or anything that touches the window manager. There are some exceptions: GUI functions that only manipulate data but don’t touch the window manager can be called on secondary threads, things like QImage and QPainter. Be careful though, as classes like QBitmap or QPixmap actually are not safe. Check the documentation for each API: if you don’t see a note at the top of the documentation saying that the function is reentrant, it’s not safe to be called except from the main thread.

3. Don’t block the main thread

Don’t call any function that can block for an unspecified amount of time on the main thread (like QThread::wait()). Since these functions stop the main thread from running, all event processing halts and your UI freezes. If you wait long enough, the OS application manager will think your app is frozen and ask the user if they want to kill it – not cool. Both are recipes for an unfriendly app.

4. Always destroy QObjects on the thread that owns them

Qt isn’t designed to allow you to destroy a QObject from any thread that doesn’t own it. That means that before a QThread is destroyed, all QObjects that the thread owns need to be destroyed first. Failing to clean up properly can cause data integrity issues, like the ever popular memory leaks and/or crashes.

How do you ensure the correct thread is the one destroying your QObject? Either create it as an automatic variable inside the QThread’s run() method, connect QThread::finished() to QObject::deleteLater(), or delay the destruction by moving the QObject to another thread with moveToThread(). Note that once you move a QObject away from the thread that owns it, you cannot touch it any more using that thread; you must use the new thread that owns the object.

5. Don’t trust your intuition when it comes to synchronization

A very common design pattern is that one thread signals its status to a monitoring thread, usually by writing to a boolean state variable that the monitoring thread can poll. With a data structure of a single word, only one thread writing to it, and only one thread reading it, it seems like this situation wouldn’t actually require concurrency protection since the read is guaranteed to happen eventually, right? Actually, even this simple case isn’t safe.

The C++ standard says that thread synchronization is mandatory and anything outside of the specification can result in undefined behaviour. If you’re not synchronizing – even in a “simple” case – you’re asking for trouble. In fact, some serious bugs have been found in the Linux kernel, in situations exactly as described here. The best thing to do is to not overthink what is safe or not – if there are concurrent accesses to the same data from multiple threads, no matter how unlikely they are to cause problems, protect them with appropriate synchronization mechanisms.

6. Act as if QObject is non-reentrant

A reentrant function means that as long as different threads are dealing with different data, it can be safely used without synchronization. The Qt documentation indicates that QObject is reentrant, but there are many caveats to this re-entrancy:

  • Event-based classes aren’t reentrant (timers, sockets, etc.)
  • Event dispatching for a given QObject happens in the thread it has affinity with; this can cause races within Qt if you touch the object from another thread
  • All QObjects in the same parent/child tree must have the same thread affinity
  • You must delete all QObjects owned by a thread before deleting the QThread
  • You can only call moveToThread() on an object from the thread the object has affinity with

To avoid all of these special cases, it’s usually easier to just act as if QObject isn’t reentrant. In practice, this means that you should only touch a QObject on the thread that owns it. This will keep you out of all the non-obvious corner cases that can cause trouble.

7. Avoid adding slots to QThread

Because QThread objects have affinity to the original thread that created them and (perhaps unintuitively) do not have affinity to themselves, this causes issues when trying to use signals and slots on a non-main thread. Although a design where a non-main thread uses a slot can be done, since it needs to side-step a lot of non-obvious gotchas our recommendation is that you just avoid this design.

If you don’t need to override QThread:run(), then don’t subclass QThread at all; just create an instance of it and you’ll avoid problems with slots (see links at the end of this blog post for my talk for how to do this with workers).

8. Use standard library threads if it’s more natural

Finally, both the C++ standard library as well as other third party libraries have a wide array of threading classes that aren’t part of Qt – parallel algorithms, coroutines, latches, barriers, atomic smart pointers, continuations, executors, concurrent queues, distributed counters, and the like.

Qt’s multi-threading capabilities are still better in some cases: for example, Qt has thread pools while the C++ standard still does not. The good news is that the C++ classes are all compatible with Qt and can be freely incorporated into your code. In fact, unless a thread manipulates QObjects and you must use Qt threads, either C++ or Qt threading classes can be used depending on what you prefer.


If you liked this short summary, you may want to watch my full QThread talk given at QtCon or see my presentation slides on this topic. These delve much deeper into the reasons behind these rules, as well as providing code samples for the dos and don’ts.

About KDAB

If you like this blog and want to read similar articles, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

FacebookTwitterLinkedInEmail

Categories: KDAB on Qt / Qt / Technical

7 thoughts on “The Eight Rules of Multithreaded Qt”

  1. I’m confused by #6 (the rest of the rules lgtm). You say “act as if QObject is non-reentrant” but then say “In practice, this means that you should only touch a QObject on the thread that owns it”. In my mind, “only touching a QObject on the thread that owns it” is exactly what reentrant means. If it were non-reentrant, I could only use that non-reentrant *type* on _one_ thread and _only_ one thread (for example if the class has static data members). Additionally you claim “Event-based classes aren’t reentrant (timers, sockets, etc.)”, but the Qt doc says “In multithreaded applications, you can use QTimer in any thread that has an event loop”. If QTimer were non-reentrant, this would not be the case: you could only instantiate QTimer classes on a single thread (probably the main thread).

    I completely agree that “you should only touch a QObject on the thread that owns it”, but don’t think that translates to “act[ing] as if QObject is non-reentrant”.

    Am I missing something?

    1. Giuseppe D'Angelo

      Hi. Let me elaborate a bit more about what I meant.

      In my mind, “only touching a QObject on the thread that owns it” is exactly what reentrant means.

      The Qt definition of reentrant for a function is that the function can be called by multiple arbitrary threads, at the same time, on different data, without any need for synchronization. The function can be called by multiple threads on the same data, but that requires external synchronization (that is, the user is responsible for synchronizing). You can find the definition here; and indeed watch out, it’s not the same definition applied elsewhere in literature!

      The same Qt definition is extended to classes: a class is reentrant if it’s safe to call its member functions (methods) on different objects, from multiple arbitrary threads, at the same time, without synchronization; calling methods on the same object at the same time requires the user to synchronize.

      For instance, QString is reentrant according to this definition: different threads can call QString functions on different QString objects at the same time without any synchronization required. If however you want to use the same QString object at the same time from multiple threads you will need to add synchronization yourself.

      My point is that QObject does not follow the definition. Even if you do synchronize your accesses to the same QObject, you’re simply not allowed to do certain operations on a QObject from a thread different than that object’s affinity thread — these operations will fail / spit warnings / crash / etc. (Emphasis on the “certain”. In the strict sense, if you don’t use those operations, then synchronization will indeed work; that’s why QObject is marked as reentrant in its documentation.)

      In a nutshell: what I’m saying is that, in the general sense, QObject violates the above definition of “reentrant”; and thus it is easier to be considered non-reentrant.

      If it were non-reentrant, I could only use that non-reentrant *type* on _one_ thread and _only_ one thread

      Once we’re in non-reentrancy territory, there are in Qt two main sub-categories. I don’t have good names for them so I won’t attempt a definition, but basically:

      1. Classes whose objects can be used from one thread and one thread only (e.g. GUI classes, usable only from the main thread)
      2. Classes whose objects can be used from one thread and one thread only, but that thread depends on the individual instance, and can potentially change (=> QObject)

      So I think you’re referring to 1. when I’m actually referring to 2. Both are correct. And both are wrong 🙂

      (To nitpick, 1. is actually just a byproduct of those classes using global resources — such as the connection to the display server — without any synchronization. Since you open that connection in the main thread, that’s what then pins all those classes to the main thread as well).

      Finally,

      If QTimer were non-reentrant, this would not be the case: you could only instantiate QTimer classes on a single thread (probably the main thread).

      This is another case of non-reentrancy of type 2. above.

      Hope this clarifies a few things, thanks for the comment!

      1. Ahh ok. You’re saying that adding synchronization doesn’t allow you to access QObject’s from threads to which they don’t belong. I agree and that makes sense. It’s a peculiar use of the word ‘reentrant’ though: different threads with different data *can* call the same methods at the same time (which means it _is_ reentrant :-P).

        You said *act* as if they aren’t reentrant: but doing so would mean only accessing a specific *type* from a single thread…

        No need to further explain etc, I understand why I was confused now… but you might want to re-word the post a little to make it clearer.

        1. Giuseppe D'Angelo

          No problem. I think the source of confusion was just a slightly different definition for the word “reentrant”. I’m using it in t “Qt sense”, but that’s not universal.

  2. Hi, is it safe to only read QT UI data from a worker thread (without modification)? Say I have a login form with lineEdit widget where a user has to enter a password. Then I block (disable the windows) and spawn a thread to verify the password (say it takes time). Can this thread read password directly from the widget or not?

    If not, then I believe I have to dynamically allocate QString containing password on the GUI thread, detach it from the GUI thread, attach it in the worker thread, finally delete the QString object on the worker thread?

    1. Giuseppe D'Angelo

      Hi,

      Although it “might” work, the official answer is no, you are not allowed to do that. As you suggest, you can just read the line edit’s contents (as a QString) and give it to the thread when you spawn it. I’m a big confused by the “detach” part, which you don’t really need — just copy or move the string into the thread, and that’s it:

      QString password = lineEdit->text();
      auto thread = QThread::create( [password]() { use(password); } );
      thread->start();
      
      1. Thank you. I thought about a C-style passing of a pointer to an argument to a thread. Indeed, C++ lambda is the right way.

Leave a Reply

Your email address will not be published. Required fields are marked *