Buscar..


Observaciones

Algunas notas que ya se mencionan en los documentos oficiales aquí y aquí :

  • Si un objeto tiene un padre, tiene que estar en el mismo hilo que el padre, es decir, no se puede mover a un hilo nuevo, ni se puede establecer un padre a un objeto si el padre y el objeto viven en hilos diferentes.
  • Cuando un objeto se mueve a un nuevo hilo, todos sus hijos también se mueven al nuevo hilo
  • Solo puedes empujar objetos a un nuevo hilo. No puede arrastrarlos a un nuevo hilo, es decir, solo puede llamar a moveToThread desde el hilo en el que el objeto vive actualmente.

Uso básico de QThread

QThread es un identificador de un hilo de plataforma. Le permite administrar el hilo monitoreando su vida útil y solicitando que finalice su trabajo.

En la mayoría de los casos no se recomienda heredar de la clase. El método de run predeterminado inicia un bucle de eventos que puede enviar eventos a objetos que viven en la clase. Las conexiones de ranura de señal entre hilos se implementan mediante el envío de un QMetaCallEvent al objeto de destino.

Una instancia de QObject se puede mover a un subproceso, donde procesará sus eventos, como eventos de temporizador o llamadas de ranura / método.

Para trabajar en un hilo, primero cree su propia clase de trabajador que se deriva de QObject . Luego muévelo al hilo. El objeto puede ejecutar su propio código automáticamente, por ejemplo, utilizando 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);
    }
};

Si su trabajador debería ser efímero y solo existir mientras se realiza su trabajo, es mejor enviar un functor o un método seguro para subprocesos para su ejecución en el grupo de subprocesos a través de QtConcurrent::run .

QtConcurrent Run

Si considera que la gestión de QThreads y primitivas de bajo nivel como mutex o semáforos es demasiado compleja, lo que está buscando es el espacio de nombres concurrente de Qt. Incluye clases que permiten una mayor gestión de hilos de alto nivel.

Echemos un vistazo a la carrera concurrente. QtConcurrent::run() permite ejecutar la función en un nuevo hilo. ¿Cuándo te gustaría usarlo? Cuando tienes alguna operación larga y no quieres crear un hilo manualmente.

Ahora el código:

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

Así que las cosas son simples: cuando necesitamos ejecutar otra función en otro subproceso, simplemente llame a QtConcurrent::run , pass function y sus parámetros, ¡y eso es todo!

QFuture presenta el resultado de nuestro cálculo asíncrono. En el caso de QtConcurrent::run no podemos cancelar la ejecución de la función.

Invocando ranuras de otros hilos

Cuando se usa un bucle de eventos Qt para realizar operaciones y un usuario que no es de Qt-saavy necesita interactuar con ese bucle de eventos, escribir la ranura para manejar invocaciones regulares de otro hilo puede simplificar las cosas para otros usuarios.

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow