Search code examples
multithreadingqtqthread

Do QThreads run on parallel?


I have two threads running and they simply print a message. Here is an minimalistic example of it.

Here is my Header.h:

#pragma once

#include <QtCore/QThread>
#include <QtCore/QDebug>

class WorkerOne : public QObject {
    Q_OBJECT
public Q_SLOTS:
    void printFirstMessage() {
        while (1) {
            qDebug() << "<<< Message from the FIRST worker" << QThread::currentThreadId();
        }
    }
};

class WorkerTwo : public QObject {
    Q_OBJECT
public Q_SLOTS:
    void printSecondMessage() {
        while (1) {
            qDebug() << ">>> Message from the SECOND worker" << QThread::currentThreadId();
        }
    }
};

And, of course, my main:

#include <QtCore/QCoreApplication>

#include "Header.h"


int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    WorkerOne kek1;
    QThread t1;
    kek1.moveToThread(&t1);
    t1.setObjectName("FIRST THREAD");

    QThread t2;
    WorkerTwo kek2;
    kek2.moveToThread(&t2);
    t2.setObjectName("SECOND THREAD");

    QObject::connect(&t1, &QThread::started, &kek1, &WorkerOne::printFirstMessage);
    QObject::connect(&t2, &QThread::started, &kek2, &WorkerTwo::printSecondMessage);

    t1.start();
    t2.start();

    return a.exec();
}

When I start application I see an expected output of it: enter image description here

As you may see, thread id is different. It's was added to be sure they are running on different threads.

I set the only one breakpoint in printFirstMessage and run the application in debug mode attached to the debugger. Once the debugger stops at my breakpoint - I wait for a while and press Continue, so my debugger stops at the same breakpoint again.

What do I expect to see? I expect to see only one <<< Message from the FIRST worker and a lot of messages from the second worker. But what do I see? I see only two messages: the first one from the first worker and the second one from the second worker.

enter image description here

I pressed Continue a lot of times and the result is more or less the same. That's weird to me, because I expected the second thread to be running while the first one is stopped by debugger.

I decided to test it using std::thread and wrote the following code:

#include <thread>
#include <iostream>

void foo1() {
    while (true) {
        std::cout << "Function ONE\n";
    }
}

void foo2() {
    while (true) {
        std::cout << "The second function\n";
    }
}

int main() {
    std::thread t1(&foo1);
    std::thread t2(&foo2);

    t1.join();
    t2.join();
}

Set a breakpoint in the first one, starts the app, after stopping at the breakpoint I hit Continue and see that console contains a lot of messages from the second function and only one from the first function (exactly this I expected using QThread as well): enter image description here

Could someone explain how does it works with QThread? By the way, I tested it using QtConcurrent::run instead of QThread and the result was as expected: the second function is running while the first one is stopped because of a breakpoint.


Solution

  • Yes, multiple QThread instances are allowed to run in parallel. Whether they effectively run in parallel is up to your OS and depends on multiple factors:

    • The number of physical (and logical) CPU cores. This is typically not more than 4 or 8 on a consumer computer. This is the maximum number of threads (including the threads of other programs and your OS itself) that can be effectively run in parallel. The number of cores is much lower than the number of threads typically running on a computer. If your computer consists of only 1 core, you will still be able to use multiple QThread's but the OS scheduler will alternate between executing those threads. QThread::idealThreadCount can be used to query the number of (logical) CPU cores.

    • Each thread has a QThread::Priority. The OS thread scheduler may use this value to prioritize (or de-prioritize) one thread over another. A thread with a lower priority may get less CPU time than a thread with a higher priority when the CPU cores are busy.

    • The (workload on the) other threads that are currently running.

    Debugging your program definitely alters the normal execution of a multi thread program:

    • Interrupting and continuing a thread has a certain overhead. In the meantime, the other threads may still/already perform some operations.
    • As pointed out by G.M., most of the time all threads are interrupted when a breakpoint is hit. How fast the others threads are interrupted is not well defined.
    • Often a debugger has a configuration option to allow interrupting a single thread, while the others continue running, see f.ex. this question.
    • The number of loops that are executed while the other thread is interrupted/started again, depends on the number of CPU instructions that are needed to perform a single loop. Calling qDebug() and QThread::currentThreadId() is definitely slower than a single std::cout.

    Conclusion: You don't have any hard garanty about the scheduling of a thread. However, in normal operation, both threads will get almost the same amount of CPU time on average as the OS scheduler has no reason to favor one over the other. Using a debugger completely alters this normal behavior.