简体   繁体   中英

QThread::create running on UI Thread

I need multi-threading in my application. According to Qt's docs, there are a number of ways to achieve this.

To briefly overview QThread ing methods:

  1. subclass QThread and reimplement run() ( doc ).

  2. Create a object inheriting from QObject with Q_OBJECT macro (for signals/slots) with doWork method, create a QThread object, use QObject::moveToThread(QThread*) and call QThread::start() ( docs , wiki )

  3. use QThread::create(Function &&f) or QThread::create(std::function<>) for lambda syntax.

I have opted to test using the 3rd option. I have also implemented a multithreading library called QThreading based on option 2, using a QWorkerThread which acts as a controller object with a QThread and QObject as objects. This library also gives the same results as shown below.


Now that the docs are out of the way, the question.

Using QThread::create(Function &&f) , I am testing to see if the QThread runs separately from the UI thread. A MCVE example below:

#include <QCoreApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qsrand(0);
    QThread *thread = nullptr;
    auto threadTest = std::function<void ()> ([&]() {

        if(thread->thread() == a.thread()) {
            qDebug() << "UI Thread in use";
        } else {
            qDebug() << "Worker thread in use";
        }

        for (int var = 0; var < INT_MAX; ++var) {
            int r = qrand() % 100;
            thread->thread()->msleep(r);
            qDebug() << "[Worker Thread " << thread->thread()->currentThreadId() << "] " << r;
        }
    });
    thread = QThread::create(threadTest);
    thread->start();

    for (int var = 0; var < INT_MAX; ++var) {
        // sleep thread 0-100ms
        int r = qrand() % 100;
        a.thread()->msleep(r);
        qDebug() << "[UI Thread " << a.thread()->currentThreadId() << "] " << r;
    }

    return a.exec();
}

The output is:

UI Thread in use
[Worker Thread  0x47e4 ]  41
[UI Thread  0x10c0 ]  38
[UI Thread  0x10c0 ]  19
[UI Thread  0x10c0 ]  38
[Worker Thread  0x47e4 ]  67
[UI Thread  0x10c0 ]  37
[Worker Thread  0x47e4 ]  34
[Worker Thread  0x47e4 ]  0
[UI Thread  0x10c0 ]  55
[Worker Thread  0x47e4 ]  69
[Worker Thread  0x47e4 ]  24
[UI Thread  0x10c0 ]  97
[Worker Thread  0x47e4 ]  78
[UI Thread  0x10c0 ]  65
[Worker Thread  0x47e4 ]  58
[UI Thread  0x10c0 ]  85
[Worker Thread  0x47e4 ]  62
[UI Thread  0x10c0 ]  50
[Worker Thread  0x47e4 ]  64
[UI Thread  0x10c0 ]  12
[Worker Thread  0x47e4 ]  5
[Worker Thread  0x47e4 ]  45

Things to note

  • UI Thread ID: 0x10c0

  • Worker Thread ID: 0x47e4

Where the concern comes in

UI Thread in use

What confuses me is the different thread addresses, yet the worker thread is still equal to the UI thread.

That leaves me to 2 explanations:

  1. QThread::currentThread always returns the host / main thread ( implausible, makes the function somewhat pointless )

  2. The QThread *thread lives inside of the Main UI Thread and thus (getting the parent thread) always returns the parent thread ie ParentThread == WorkerThread (ParentThread)

Do I miss understand how the QThread works?

  1. If you want to know if it runs in the main thread then you should compare the currentThread with the QCoreApplication thread:
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
    qDebug() << "UI Thread in use";
} else {
    qDebug() << "Worker thread in use";
}
  1. QThread is not a thread is a thread handler as the docs points out:

Detailed Description

A QThread object manages one thread of control within the program. QThreads begin executing in run() . By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.

In addition, QThread is a QObject that lives in the parent's thread or where it was created if it has no parent as the docs points out:

Thread Affinity

A QObject instance is said to have a thread affinity, or that it lives in a certain thread. When a QObject receives a queued signal or a posted event, the slot or event handler will run in the thread that the object lives in.

Note: If a QObject has no thread affinity (that is, if thread() returns zero), or if it lives in a thread that has no running event loop, then it cannot receive queued signals or posted events.

By default, a QObject lives in the thread in which it is created. An object's thread affinity can be queried using thread() and changed using moveToThread().

All QObjects must live in the same thread as their parent. Consequently:

  • setParent() will fail if the two QObjects involved live in different threads.
  • When a QObject is moved to another thread, all its children will be automatically moved too.
  • moveToThread() will fail if the QObject has a parent.
  • If QObjects are created within QThread::run(), they cannot become children of the QThread object because the QThread does not live in the thread that calls QThread::run().

A QObject's member variables do not automatically become its children. QObject 的成员变量不会自动成为它的子变量。 The parent-child relationship must be set by either passing a pointer to the child's constructor, or by calling setParent(). Without this step, the object's member variables will remain in the old thread when moveToThread() is called.

In your case "thread" object is a QObject that lives in the main thread since it has no parent and there it was created and that handles another thread.

Update:

MWE

#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qsrand(0);
    auto threadTest = std::function<void ()> ([&]() {
        if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
            qDebug() << "UI Thread in use";
        } else {
            qDebug() << "Worker thread in use";
        }

        for (int var = 0; var < INT_MAX; ++var) {
            int r = qrand() % 100;
            QThread::msleep(r);
            qDebug() << "[Worker Thread " << QThread::currentThreadId() << "] " << r;
        }
    });
    QThread *thread = QThread::create(threadTest);
    thread->start();

    int var = 0;
    std::function<void ()> timerTest;
    timerTest = [&](){
        int r = qrand() % 100;
        qDebug() << "[UI Thread " << QThread::currentThreadId() << "] " << r;
        ++var;
        if (var < INT_MAX)
            QTimer::singleShot(r, timerTest);
    };
    int r = qrand() % 100;
    QTimer::singleShot(r, timerTest);

    return a.exec();
}

Output:


[UI Thread  0x7fc6222993c0 ]  94
[Worker Thread  0x7fc621f62700 ]  71
[UI Thread  0x7fc6222993c0 ]  86
[Worker Thread  0x7fc621f62700 ]  94
[UI Thread  0x7fc6222993c0 ]  37
[Worker Thread  0x7fc621f62700 ]  86
...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM