QThread :: create работает в потоке пользовательского интерфейса - PullRequest
1 голос
/ 22 октября 2019

Мне нужно многопоточность в моем приложении. Согласно документации Qt, есть несколько способов достичь этого.

Краткий обзор QThread методов:

  1. подкласс QThreadи переопределите run () ( doc ).

  2. Создайте объект, наследуемый от QObject с макросом Q_OBJECT (для сигналов / слотов) с doWorkметод, создайте объект QThread, используйте QObject :: moveToThread (QThread *) и вызовите QThread::start() ( документы , вики )

  3. использование QThread :: create (Function && f) или QThread::create(std::function<>) для синтаксиса лямбды.

Я решил проверить с помощью3-й вариант. Я также реализовал многопоточную библиотеку под названием QThreading , основанную на варианте 2, используя QWorkerThread , который действует как объект контроллера с QThread и QObject в качестве объектов. Эта библиотека также дает такие же результаты, как показано ниже.


Теперь, когда документы не нужны, вопрос.

Используя QThread::create(Function &&f), я проверяю, работает ли QThread отдельно от потока пользовательского интерфейса. A MCVE пример ниже:

#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();
}

Выход:

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

Примечания

  • Идентификатор пользовательского интерфейса: 0x10c0

  • Идентификатор рабочей резьбы: 0x47e4

Когда возникает проблема

Используемый поток пользовательского интерфейса

Меня смущает то, что разные адреса потоков, но рабочий поток по-прежнему равен в поток пользовательского интерфейса.

Это дает мне два объяснения:

  1. QThread::currentThread всегда возвращает хост / основной поток ( неправдоподобно, делает функциюнесколько бессмысленно )

  2. QThread *thread живет внутри основного потока пользовательского интерфейса и, таким образом (получая родительский поток) всегда возвращает родительский поток, т. е. ParentThread == WorkerThread (ParentThread)

Неужели я не понимаю, как работает QThread?

1 Ответ

2 голосов
/ 23 октября 2019
  1. Если вы хотите знать, работает ли он в главном потоке, вам следует сравнить currentThread с потоком QCoreApplication:
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
    qDebug() << "UI Thread in use";
} else {
    qDebug() << "Worker thread in use";
}
QThread не является потоком, является обработчиком потока, как документы указывает:

Подробное описание

A QThread объект управляет одним потоком управления в программе. QПотоки начинают выполняться в run(). По умолчанию run () запускает цикл обработки событий, вызывая exec () и запускает цикл обработки событий Qt внутри потока.

Кроме того, QThread является объектом QObject, который находится в потоке родителя или там, где онбыл создан, если у него нет родителя, как указано в документах :

Сходство потоков

Считается, что экземпляр QObject имеет сходство с потоком или что онживет в определенной теме. Когда QObject получает поставленный в очередь сигнал или опубликованное событие, слот или обработчик события запускается в потоке, в котором находится объект.

Примечание. Если QObject не имеет сродства к потоку (т. Е. Если поток () возвращает ноль), или если он находится в потоке, у которого нет работающего цикла обработки событий, он не сможет получать поставленные в очередь сигналы или опубликованные события.

По умолчанию QObject живет в потоке, в котором он создан. Сходство с потоком объекта можно запросить с помощью thread () и изменить с помощью moveToThread ().

Все объекты QObject должны находиться в том же потоке, что и их родительский объект. Следовательно:

  • setParent () завершится ошибкой, если два задействованных объекта QObject живут в разных потоках.
  • Когда объект QObject перемещается в другой поток, все его дочерние элементы также будут автоматически перемещаться.
  • moveToThread () завершится ошибкой, если у QObject есть родительский элемент.
  • Если QObject созданы в QThread :: run (), они не могут стать дочерними для объекта QThread, поскольку QThread не живетв потоке, который вызывает QThread :: run ().

Примечание: Переменные-члены QObject не становятся автоматически его дочерними. Родительско-дочерние отношения должны быть установлены либо путем передачи указателя на дочерний конструктор, либо путем вызова setParent (). Без этого шага переменные-члены объекта останутся в старом потоке при вызове moveToThread ().

В вашем случае объект «поток» - это объект QObject, который живет в основном потоке, поскольку у него нетродитель и там он был создан и обрабатывает другой поток.

Обновление:

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();
}

Вывод:

<b>Worker thread in use</b>
[UI Thread  0x7fc6222993c0 ]  94
[Worker Thread  0x7fc621f62700 ]  71
[UI Thread  0x7fc6222993c0 ]  86
[Worker Thread  0x7fc621f62700 ]  94
[UI Thread  0x7fc6222993c0 ]  37
[Worker Thread  0x7fc621f62700 ]  86
...
...