Skip to content

New in Qt 5.10: QThread::create

If you are using Qt and you need to run some code in a separate thread, chances are that you are using QThread for the job. QThread is a very old class in Qt, making its first appearance in Qt 2.2, released on the 22nd of September 2000. Its responsibility is to start a new thread, and let you execute code in that thread.

There are two main ways of running code in a separate thread using QThread:

  1. subclassing QThread and overriding run();
  2. creating a “worker object” (some QObject subclass) and connecting it to QThread signals.

Please refer to my Qt World Summit 2017 presentation (video) for more details about how to use these two functions. There are also lots of other documentation and blog posts available on the internet, discussing the pros and the cons of the two approaches.

Enter QThread::create()

In Qt 5.10 I have added another way to run some code in a new thread. Inspired by C++11’s std::thread, I have introduced QThread::create, a factory function that creates a new QThread which will run a user-provided callable:

QThread *thread = QThread::create([]{ runSlowCode(); });
thread->start();

The advantage of this approach is that it avoids creating a new QThread subclass manually for the sole purpose to override its run() member function and run some code.

Unlike std::thread, however, the newly-created thread is not automatically launched; the user is expected to start it with an explicit call to start(). This allows users to do some extra work before starting the thread, for instance, connect to its signals, set the name of the thread, or change its priority, as demonstrated by this snippet:

QThread *thread = QThread::create(myFunction);

thread->setObjectName("WorkerThread"); // name to appear in ps, task manager, etc.
connect(thread, &QThread::started, gui, &Gui::threadHasStarted);

thread->start();

The user acquires ownership of the newly-created QThread object.

With a C++17 capable compiler it is also possible to pass extra arguments to QThread::create(): these arguments will be then passed to the function in the new thread, using the same semantics as std::thread‘s constructor. For instance:

QThread *thread = QThread::create(myFunction, arg1, arg2);
// extra setup...
thread->start(); // calls myFunction in another thread, passing arg1 and arg2

That’s it! I hope this will be useful to you. However, the API of QThread::create() is only half of the story. If you want to know more about what this apparently little patch meant for Qt, keep reading.

Implementation notes

In afterthought, the implementation of QThread::create is very simple. However, when doing the necessary research, I found a small oddity with the C++ Standard Library that deserves some attention.

The most important constraint I imposed on myself when writing QThread::create was to be as compatible as possible with std::thread‘s constructor semantics. If one reads the relevant section in the Standard (f.i. here), one can see that the actual semantics of the invocation are quite complex. The extra arguments to the function undergo a transformation (specified as DECAY_COPY), they’re stored “somewhere”, and finally the functor is invoked using the INVOKE function. Both DECAY_COPY and INVOKE are not actual Standard Library functions, but pseudo-functions formally defined elsewhere in the C++ Standard.

An oddity that I found during my research is that the C++11 Standard does not provide these building blocks to end-users (and by that I also include developers of other C++ libraries, like Qt). A way to use INVOKE for user code got added in C++17 with the std::invoke function; however, there is simply nothing ready-made that applies the DECAY_COPY transformation, and stores the callable and its decayed/copied arguments, and possibly even invokes the function with the right semantics.

The only workaround that I have found was to use a higher-level thread primitive provided by the Standard Library: std::async. std::async has the same invocation semantics as std::thread‘s constructor; it returns a std::future that we can use to examine the result.

We can force std::async to not spawn an additional thread and to run the callable only when asked to do so, by specifying the std::launch::deferred launch policy. std::launch::deferred allows a sort of lazy evaluation: the callable is invoked only when one tries to access the result (via the returned std::future object):

auto future = std::async(std::launch::deferred, function, arg1, arg2);

// some time later, possibly in another thread:
future.get(); // calls the function with the arguments

This is exactly how QThread::create() is implemented: it calls std::async on the user-provided function and its arguments, and stores the returned std::future in an instance of an internal QThread subclass. This subclass overrides run(), which in turn simply calls get() on the future (see here and here). This machinery isn’t zero-overhead, as the combination of std::async and std::future does way more than what we need, but it at least shifts the burden of the implementation from Qt onto the Standard Library.

The extra plumbing needed is the aforementioned std::invoke, which however is only available in C++17. Under a C++17 compiler, QThread::create() takes a callable and its arguments, whereas under a pre-C++17 compiler, QThread::create() only supports a callable (and no further arguments). In practice this isn’t a big limitation, because one can always use a lambda to wrap the callable and its arguments, and then pass the lambda as the only argument.

Qt and the Standard Library ABI

The addition of QThread::create() to Qt was also possible because of a major policy change in Qt.

A little bit of history: up to and including version 4, Qt did not require the presence of a Standard Library implementation on the system (the configure script had a -no-stl option). From version 5.0 Qt started to require a working Standard Library implementation: the configure switch was dropped, and Qt code dealing with Standard Library types was compiled unconditionally. Qt itself started using Standard Library types (notably, containers) in its own implementation, mostly where such types provided more features or performance when compared to Qt’s own versions.

However, Qt has never exposed Standard Library types from its public ABI: all functions taking or returning Standard Library types had to be inline (example). The main reason for doing so was to make the same build of Qt compatible across different Standard Library implementations (e.g. libc++ and stdlibc++, or two ABI-incompatible builds of the same implementation).

After lots of debate, this has finally changed in Qt 5.10: Qt is now allowed to expose Standard Library types from its public ABI. Users wishing to mix-and-match Standard Library implementations need to recompile Qt. (In other words, we’ve made this Someone Else’s Problem).

What does this have to do with QThread::create()? As I explained before, the current implementation uses std::async under the hood. The std::future returned by std::async is then passed to an exported, out-of-line Qt function, which constructs and returns the result — the QThread instance that runs the user-supplied function. (The reason for doing such a thing is because QThread is an exported polymorphic class. A subclass defined entirely inline would generate copies of its vtable / type_info in all the translation units using it, and this may cause problems.) Therefore, now std::future is part of Qt’s own ABI; and I’m very glad I didn’t have to re-implement it!

Categories: C++ / KDAB Blogs / KDAB on Qt / Qt / QtDevelopment

13 thoughts on “New in Qt 5.10: QThread::create”

  1. Hi, when using QThread::create, no event loop is executed in the created thread ? Also, does the thread exits when the function returns ?

    1. Giuseppe D'Angelo

      Hi,

      yes, the thread exists when the function returns. And yes, there is no event loop running in the created thread: the thread will just run the function. If you need an event loop, you can simply create an instance of QEventLoop in your function and call exec() on it (after you’ve done all the necessary setup). Hope this helps!

        1. Giuseppe D'Angelo

          At the moment the documentation is not visible/generated because of what seems to be a qdoc bug (QTBUG-65524). I’m not sure how to improve it regarding the lack of event loop — given that it runs your function, and only your function. Feel free to submit ideas on Qt’s own bugtracker, in the documentation component. Thanks!

  2. Awesome article, thanks a lot!
    I’m glad to see Qt becoming more and more consistent with the rest of C++.
    I wonder whether it will start adopting shared_ptrs as well, it’s about time.

    1. Giuseppe D'Angelo

      Thanks for the feeback.
      About std::shared_ptr: where do you see its usage in Qt APIs? Note that you can perfectly use it in your code. QSharedPointer / QWeakPointer are de facto deprecated these days (see the notes from the Contributors’ Summit 2017). They can’t get offically deprecated because they’re used in public APIs, and those cannot be changed.

      1. Well, Qt haters ™ usually complaint about the parent/child relationships of QObject, which is like asking for a shared/weak ptr construct. At least, that’s how I would implement such a relationship for a non – Qt program.
        Not that I don’t like the current implementation, as a matter of fact I do, it’s easy to use, and it’s not that I think it needs to be replaced with a std::shared_ptr construct, it’s just something that I would expect.
        Sort of.

  3. Great to see that! But I’m thinking why not to use simple constructors instead of static factory methods in the API? I wish to use this more readable and object oriented form:
    QThread *thread = new QThread([]{ runSlowCode(); });
    thread->start();

    Or the more Qt form:
    QThread *thread = new QThread([]{ runSlowCode(); }, this);
    thread->start();
    Then the thread should be stopped and deleted on destruction of “this”.
    There are no technical excuse to use static factory methods as you can see here:
    http://www.yegor256.com/2017/11/14/static-factory-methods.html

    1. Giuseppe D'Angelo

      Hi,
      Thanks for the feedback. Yes, this was given some consideration. In the end it was opted for a static factory, basically for consistency reasons with the rest of Qt, in the hope to find a compromise between maximum compatibility with both Qt and std::thread. For instance, in your very example, there would be a huge surprise factor: is the “this” argument to the constructor the parent object, or is it an argument to be passed to the new function when starting the new thread? People would expect the former, but it is the latter instead. Thus, it was decided to stay away from it. Hope this clarifies things a little bit 🙂

      1. Thanks your for the explanation.
        Now understand that’s the best option, but QThread continues to seems very hard to understand and obscure to me, and worst, is growing :(, I hope Qt6 will give us a new point of view. Maybe in my next project should be better to use QFuture.

        Anyway, thank you for all the work.

Leave a Reply

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

By continuing to use the site, you agree to the use of cookies. More information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close