QThread vs std::thread

13,539

Solution 1

QThread is not just a thread, it is also a thread manager. If you want your thread to play Qt, then QThread is the way to go. Qt is event driven, as is most of modern programming. That's a little more complex and flexible than "make a thread run a function".

In Qt, you'd typically create a worker together with a QThread, move the worker to that thread, then every function invoked by the event system for that worker object will be executed in the thread the worker object has affinity to.

So you can encapsulate your functionality in different worker object, say SafetyChecker, Printer, ServoDriver, JetDriver and so on, create an instance of each, move it to a dedicated thread and you are set. You still can invoke functions that would "block" instead of using fine grained events, and use atomics or mutexes to do inter-thread synchronization. There is nothing wrong with that, as long as you don't block the main/gui thread.

You would probably not want to do your printer code event driven, since in a multithreaded scenario that would involve queued connections, which are marginally slower than direct connections or even virtual dispatch. So much so that if you make your multithreading too fine grained, you are likely to actually experience a huge performance hit.

That being said, using Qt's non-gui stuff has its own merits, it will allow you to make a cleaner and more flexible design much easier, and you will still get the benefits of multithreading if you implement things properly. You can still use an event driven approach to manage the whole thing, it will be significantly easier than using only std::thread, which is a much lower level construct. You can use event driven approach to setting up, configuring, monitoring and managing the design, while the critical parts can be executed in blocking functions in auxiliary threads to achieve fine grained control at the lowest possible synchronization overhead.

To clarify - the answer does not focus on async task execution, because the other two answers already do, and because as I mentioned in the comments, async tasks are not really intended for control applications. They are suited to the execution of small tasks, which still take more time than you'd want to block the main thread for. As a recommended guideline, everything that takes more than 25 msec is preferable to be executed async. Whereas printing is something that may take minutes or even hours, and imply continuously running control functions, in parallel and using synchronization. Async tasks won't give you the performance, latency and order guarantees for control applications.

Solution 2

QThread is nice if you want to integrate the thread into the Qt system (like having to emit signals or connect to certain slots)

Though the layout of QThread is still made so it works with "old" c++. You have to create a class and all this overhead (code and typing wise) just for running in a thread.

If you just simply want to start a thread I think c++11 std::thread is less verbose/code you have to write. You can just use a lambda or function pointer and can give as many arguments as you want. So for simple threading I would suggest the c++11 threads over the QThreads.

Ofcourse it can be a matter of opinion which of those you prefer.


Though Qt has several different high level thread objects C++ does not. You may want to look into these if you need some of those instead of a basic thread or maybe you don't really need a basic thread at all but those suit you better.

Like a QThreadPool or simply a QTimer if you need to wait for things. Here is some readup about the alternatives in Qt to a bare thread.

QConcurrent also comes more close to c++11 future and async for example and has also optional threadpools where it can run.

Solution 3

The major problem with std::thread and QThread is that it does what it says on the tin: creates one thread for you, a thread that likely will do just one thing. Running a function "concurrently" using std::thread is very wasteful: threads are expensive resources, so creating one just to run some function is usually overkill.

While std::thread t1(&Class::function, this, ...) looks nice and all, it is usually a premature pessimization and suggesting it as some universal way of "doing things concurrently" is IMHO wrong. You can do better than that.

  1. If you want to run a function/functor/method concurrently in a worker thread, use QtConcurrent::run or std::async.

    QtConcurrent::run uses the default thread pool by default, you can also pass your own instance of QThreadPool. A typical case would be to use the default thread pool for CPU-bound tasks, e.g. computations, image transformations, rendering, etc., and use a dedicated, larger I/O thread pool to do operations that are blocking due to limitations of the APIs you're forced to use (e.g. many database libraries only offer blocking APIs because their designs are fundamentally broken). Example:

    // interface
    QThreadPool * ioPool();
    
    // implementation
    Q_GLOBAL_STATIC(QThreadPool, ioPool_impl);
    QThreadPool * ioPool() { return ioPool_impl; }
    
  2. If you want to have a QObject live in another thread (perhaps co-habitating with other objects), use QThread then move your object to that thread using moveToThread.

    It is an idiom to emit a signal from the worker thread to thread-safely pass data to the main thread. E.g. suppose you want to have a responsive GUI and wish to load images from disk in a worker thread:

    class MyWidget : public QWidget {
      QLabel m_label;
      ...
      Q_SIGNAL void setImage(const QImage &);
     public:
      MyWidget() {
       ...
       connect(MyWidget, &MyWidget::setImage, this, [this](const QImage & image){
        m_label.setPixmap(QPixmap::fromImage(image));
       });
       QtConcurrent::run(ioPool(), [this]{ setImage({"/path/to/image.png"});  });
      }
    };
    
Share:
13,539

Related videos on Youtube

ElevenJune
Author by

ElevenJune

Updated on June 04, 2022

Comments

  • ElevenJune
    ElevenJune almost 2 years

    I saw different topics on "pthread vs std::thread" and "QThread vs pthread" but none on "std::thread vs QThread".

    I have to program a software to drive a 3D Printer and need to use threads. There will be a thread that will check safety constantly, another to execute the printing process, some for driving each hardware component (movement, jet, ...) separately, etc... The program is developed for Windows with C++11/Qt.

    First I wanted to use QThread, but it seems to me that QThread does not allow you to do as many things as std::thread, for instance, while reading "C++ Concurrency in Action" by Anthony Williams, I saw that it was possible to ask a std::thread to execute a function from another thread by doing something like std::thread t1(&Class::function, this, ...); which does not seem to be possible with QThread.

    The mechanism I would like to have most is a way of saying if I want a function to be executed in the current thread or in another thread.

    Which one would you choose to do that and why ?

    • Galik
      Galik over 7 years
      I would not want to tie my printing code to a specific GUI Library unless I was forced to. I don't see any disadvantages to using std::thread for as much as possible.
    • infixed
      infixed over 7 years
      Alternately, if one's development environment didn't support std::thread, like say an older version of MSVC, the Qt program may be source code transportable where a std::thread application would not be.
    • dtech
      dtech over 7 years
      @Galik - Qt is not a GUI library, it is an application development framework, GUI is a small part of it. There is nothing wrong with using the rest of Qt for core logic instead of std::stuff, for example Qt containers are just as fast and also easier and faster to work with, because their usage is not as verbose.
    • Galik
      Galik over 7 years
      @ElevenJune Only you know your needs. One thing to consider is that it can be useful to build up a body of code that you can re-use in different projects that won't necessarily always use a specific library. That can make future development faster/cheaper. For this reason I tend to look for solutions within the standard C++ libraries first. You have to decide what benefits using Qt would add in this instance.
  • dtech
    dtech over 7 years
    I doubt QtConcurrent::run would be a good candidate for something like driving a printer.
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica over 7 years
    @ddriver If you use an I/O pool, it's perfectly fine as long as the printer can be used from another thread.
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica over 7 years
    I think that std::thread taking a functor/lambda is a horrible interface, since it encourages the creation of ephemeral threads for the purpose of running short snippets of code. Even the OP fell for that trap! QThread's API informs you that the threads are definitely not lightweight things. In practice, std::thread has no more overhead than QThread, because the real overhead is the much bigger cost of creating a thread by the OS. Whatever code you put in the helper class that wraps it is truly immaterial.
  • Kuba hasn't forgotten Monica
    Kuba hasn't forgotten Monica over 7 years
    In most cases, you should be using std::async or QtConcurrent::run, not QThread or std::thread. QThread is mostly useful to move QObjects to.
  • Hayt
    Hayt over 7 years
    @KubaOber with "overhead" I meant the amount of typing/code you need to do, not performance wise. But I should clarify that. But I agree. In most cases you should prefer the higher level API
  • dtech
    dtech over 7 years
    I'd say the intent of async execution is more suited for executing a multitude of small tasks without blocking the main/gui thread, it is not suited for control applications, which imply continuous execution while the external hardware process is finished.
  • Hayt
    Hayt over 7 years
    As I said. Most cases. When you have one thread who has to work with IO and you only need one thread a threadpool would likely be overkill and it depends on how much you need to interact with the object, etc. It's all about context.
  • ElevenJune
    ElevenJune over 7 years
    Thanks for your answer. What you are saying about encapsulating worker and moving them to a QThread is what I already did : For example, with the ServoDriver, I just have to call ServoDriver::moveTo(x,y) and the movement is done in another thread and does not block the rest of the program. The problem now is : I want to move then print something, how do I do to wait for the movement to be done before printing ?
  • dtech
    dtech over 7 years
    It sounds like you need to implement a trivial instruction queue. Then you insert commands into it. The worker executes one command, after it finishes it executes the next until the entire queue is processed. Each command can be represented by an integer, followed by its parameters, QDataStream is a good candidate to implement that.
  • ElevenJune
    ElevenJune over 7 years
    I tried it too and it worked, but I could not seem to have both behaviors in the same application.With what I did now, I can drive each component separatly at the same time. It's very usefull for the calibration GUI : you say "move there",it moves, the GUI is not blocked, great. To do that, I call ServoDriver::moveTo(x,y), which send a signal to the class representing the element I want to move (there are 4 diffrents elements just for the movement). The class has a signal handler on a different thread and executes the movement in this thread. With that implementation I can't do what you say
  • dtech
    dtech over 7 years
    Well, technically you wouldn't want to write hardware control application in a desktop os, that will actually run on a dedicated microcontroller with a real time OS, or even without an OS at all, because that guarantees you real time performance.
  • dtech
    dtech over 7 years
    In your particular case, you'd feed commands into the command queue, an interpreter will be reading in those commands and dispatching them to the appropriate worker, for example moveToXY will be represented by a op code int and x,y ints, the interpreter will read in the op code, switch it in a switch statement to the case of moveXY, read in the two parameters and send the operation to the ServoDriver, when the driver completes it, it will signal the interpreter to read in the next command, which will be sent to its respective object, and so on...
  • ElevenJune
    ElevenJune over 7 years
    Very clear reponse, I looked into QtConcurrent and it seems greats. Thank you !