Ricerca…


Osservazioni

Alcune note che sono già menzionate nei documenti ufficiali qui e qui :

  • Se un oggetto ha un genitore, deve essere nello stesso thread del genitore, cioè non può essere spostato in un nuovo thread, né è possibile impostare un genitore su un oggetto se il genitore e l'oggetto vivono in thread diversi
  • Quando un oggetto viene spostato su un nuovo thread, anche tutti i relativi figli vengono spostati nel nuovo thread
  • Puoi solo spostare oggetti su un nuovo thread. Non puoi trascinarli su un nuovo thread, ovvero puoi solo moveToThread dal thread in cui l'oggetto sta attualmente vivendo

Utilizzo di base di QThread

QThread è un handle per un thread di piattaforma. Ti consente di gestire il thread monitorandone la durata e richiedendo il completamento del lavoro.

Nella maggior parte dei casi, l'ereditarietà dalla classe non è raccomandata. Il metodo di run predefinito avvia un ciclo di eventi che può inviare eventi agli oggetti che vivono nella classe. Le connessioni dello slot del segnale cross-thread vengono implementate QMetaCallEvent un oggetto QMetaCallEvent all'oggetto target.

Un'istanza QObject può essere spostata su un thread, dove elaborerà i suoi eventi, come eventi timer o chiamate slot / metodo.

Per eseguire il lavoro su un thread, innanzitutto creare la propria classe worker che deriva da QObject . Quindi spostalo nella discussione. L'oggetto può eseguire automaticamente il proprio codice, ad esempio utilizzando 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);
    }
};

Se il tuo operatore deve essere effimero e esistere solo mentre il suo lavoro è in esecuzione, è meglio inviare un metodo o un metodo thread-safe per l'esecuzione nel pool di thread tramite QtConcurrent::run .

QtConcurrent Run

Se trovi la gestione di QThread e di primitive di basso livello come mutex o semafori troppo complessi, lo spazio dei nomi simultaneo di Qt è quello che stai cercando. Include classi che consentono una gestione dei thread di alto livello.

Diamo un'occhiata a Concurrent Run. QtConcurrent::run() consente di eseguire la funzione in un nuovo thread. Quando vorresti utilizzarlo? Quando hai qualche operazione lunga e non vuoi creare il thread manualmente.

Ora il codice:

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

Quindi le cose sono semplici: quando abbiamo bisogno di eseguire un'altra funzione in un altro thread, basta chiamare QtConcurrent::run , passare la funzione ei suoi parametri e il gioco è fatto!

QFuture presenta il risultato del nostro calcolo asincrono. In caso di QtConcurrent::run non possiamo annullare l'esecuzione della funzione.

Richiamo di slot da altri thread

Quando un ciclo di eventi Qt viene utilizzato per eseguire operazioni e un utente non-Qt-saavy deve interagire con quel ciclo di eventi, scrivere lo slot per gestire le chiamate regolari da un altro thread può semplificare le cose per gli altri utenti.

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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow