Поиск…


замечания

Несколько заметок, которые уже упоминаются в официальных документах здесь и здесь :

  • Если у объекта есть родительский элемент, он должен находиться в том же потоке, что и родительский, то есть его нельзя перенести в новый поток, и вы не можете установить родительский объект в объект, если родительский объект и объект живут в разных потоках
  • Когда объект перемещается в новый поток, все его дочерние элементы также перемещаются в новый поток
  • Вы можете только нажимать объекты на новый поток. Вы не можете вытащить их в новый поток, т. moveToThread Вы можете только вызвать moveToThread из потока, в котором объект в настоящее время живет

Основное использование QThread

QThread - это дескриптор потока платформы. Он позволяет управлять потоком, отслеживая его срок службы и запрашивая, чтобы он завершил свою работу.

В большинстве случаев наследование от класса не рекомендуется. Метод run умолчанию запускает цикл событий, который может отправлять события объектам, живущим в классе. Кросс-нитки сигнальных слотов реализуются путем отправки QMetaCallEvent на целевой объект.

Экземпляр QObject можно перенести в поток, где он будет обрабатывать свои события, такие как события таймера или вызовы слотов / методов.

Чтобы выполнить работу над потоком, сначала создайте свой собственный рабочий класс, полученный из QObject . Затем переместите его в поток. Объект может автоматически запускать свой собственный код, например, используя QMetaObject::invokeMethod() .

#include <QObject>

class MyWorker : public QObject
{
    Q_OBJECT
public:
    Q_SLOT void doWork() {
        qDebug() << "doWork()" << QThread::currentThread();
        // and do some long operation here
    }
    MyWorker(QObject * parent = nullptr) : QObject{parent} {}
};

class MyController : public QObject
{
    Q_OBJECT
    Worker worker;
    QThread workerThread;
public:
    MyController()  {
        worker.moveToThread(&workerThread);
        // provide meaningful debug output
        workerThread.setObjectName("workerThread");
        workerThread.start();
        // the thread starts the event loop and blocks waiting for events
    }
    ~MyController()  {
        workerThread.quit();
        workerThread.wait();
    }
    void operate() {
        // Qt::QueuedConnection ensures that the slot is invoked in its own thread
        QMetaObject::invokeMethod(&worker, "doWork", Qt::QueuedConnection);
    }
};

Если ваш работник должен быть эфемерным и существует только во время его работы, лучше всего представить функтор или потокобезопасный метод для выполнения в пуле потоков через QtConcurrent::run .

Прогон QtConcurrent

Если вы обнаружите, что управление QThreads и низкоуровневыми примитивами, такими как мьютексы или семафоры, слишком сложны, пространство имен Qt Concurrent - это то, что вы ищете. Он включает классы, которые позволяют управлять потоками более высокого уровня.

Давайте посмотрим на Concurrent Run. QtConcurrent::run() позволяет запускать функцию в новом потоке. Когда вы хотели бы использовать его? Когда у вас длительная операция, и вы не хотите создавать поток вручную.

Теперь код:

#include <qtconcurrentrun.h>

void longOperationFunction(string parameter)
{
    // we are already in another thread
    // long stuff here
} 

void mainThreadFunction()
{
    QFuture<void> f = run(longOperationFunction, "argToPass");
    f.waitForFinished();
}

Итак, все просто: когда нам нужно запустить другую функцию в другом потоке, просто вызовите QtConcurrent::run , pass function и его параметры, и все!

QFuture представляет результат нашего асинхронного вычисления. В случае QtConcurrent::run мы не можем отменить выполнение функции.

Вызов слотов из других потоков

Когда цикл выполнения Qt используется для выполнения операций, и пользователь, не относящийся к Qt-saavy, должен взаимодействовать с этим циклом событий, запись слота для обработки регулярных вызовов из другого потока может упростить работу для других пользователей.

main.cpp:

#include "OperationExecutioner.h"
#include <QCoreApplication>
#include <QThread>

int main(int argc, char** argv)
{
    QCoreApplication app(argc, argv);

    QThread thrd;
    thrd.setObjectName("thrd");
    thrd.start();
    while(!thrd.isRunning())
        QThread::msleep(10);

    OperationExecutioner* oe = new OperationExecutioner;
    oe->moveToThread(&thrd);
    oe->doIt1(123,'A');
    oe->deleteLater();
    thrd.quit();
    while(!thrd.isFinished())
        QThread::msleep(10);

    return 0;
}

OperationExecutioner.h:

#ifndef OPERATION_EXECUTIONER_H
#define OPERATION_EXECUTIONER_H

#include <QObject>

class OperationExecutioner : public QObject
{
    Q_OBJECT
public slots:
    void doIt1(int argi, char argc);
};

#endif  // OPERATION_EXECUTIONER_H

OperationExecutioner.cpp:

#include "OperationExecutioner.h"
#include <QMetaObject>
#include <QThread>
#include <QDebug>

void OperationExecutioner::doIt1(int argi, char argc)
{
    if (QThread::currentThread() != thread()) {
        qInfo() << "Called from thread" << QThread::currentThread();
        QMetaObject::invokeMethod(this, "doIt1", Qt::QueuedConnection,
                                  Q_ARG(int,argi), Q_ARG(char,argc));
        return;
    }

    qInfo() << "Called from thread" << QThread::currentThread()
            << "with args" << argi << argc;
}

OperationExecutioner.pro:

HEADERS += OperationExecutioner.h
SOURCES += main.cpp OperationExecutioner.cpp
QT -= gui


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow