Qt
Threading et concomitance
Recherche…
Remarques
Quelques notes déjà mentionnées dans les docs officiels ici et ici :
- Si un objet a un parent, il doit être dans le même thread que le parent, c.-à-d. Qu'il ne peut pas être déplacé vers un nouveau thread, et que vous ne pouvez pas définir un parent sur un objet si le parent et l'objet vivent dans des threads différents
- Lorsqu'un objet est déplacé vers un nouveau thread, tous ses enfants sont également déplacés vers le nouveau thread
- Vous ne pouvez que pousser des objets vers un nouveau thread. Vous ne pouvez pas les tirer vers un nouveau thread, c’est-à-dire que vous ne pouvez appeler que
moveToThread
partir du thread où l’objet vit actuellement
Utilisation basique de QThread
QThread
est un handle vers un thread de plate-forme. Il vous permet de gérer le thread en surveillant sa durée de vie et en lui demandant de terminer son travail.
Dans la plupart des cas, l'héritage de la classe n'est pas recommandé. La méthode d' run
par défaut lance une boucle d'événements pouvant envoyer des événements aux objets de la classe. Les connexions d'intervalle de signal entre threads sont implémentées en envoyant un QMetaCallEvent
à l'objet cible.
Une instance de QObject
peut être déplacée vers un thread, où elle traitera ses événements, tels que les événements du minuteur ou les appels d'emplacement / méthode.
Pour travailler sur un thread, commencez par créer votre propre classe de travail QObject
de QObject
. Puis déplacez-le sur le fil. L'objet peut exécuter son propre code automatiquement, par exemple en utilisant 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 votre agent doit être éphémère et n'existe que pendant son travail, il est préférable de soumettre un foncteur ou une méthode thread-safe pour exécution dans le pool de threads via QtConcurrent::run
.
QtConcurrent Run
Si vous trouvez que la gestion de QThreads et de primitives de bas niveau telles que les mutex ou les sémaphores est trop complexe, Qt Concurrent est ce que vous recherchez. Il comprend des classes qui permettent une gestion plus poussée des threads.
Regardons Exécution simultanée. QtConcurrent::run()
permet d'exécuter une fonction dans un nouveau thread. Quand aimeriez-vous l'utiliser? Lorsque vous avez une longue opération et que vous ne voulez pas créer de thread manuellement.
Maintenant le code:
#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();
}
Les choses sont donc simples: lorsque nous devons exécuter une autre fonction dans un autre thread, appelez simplement QtConcurrent::run
, pass function et ses paramètres et voilà!
QFuture
présente le résultat de notre calcul asynchrone. Dans le cas de QtConcurrent::run
nous ne pouvons pas annuler l'exécution de la fonction.
Invocation de slots à partir d'autres threads
Lorsqu'une boucle d'événement Qt est utilisée pour effectuer des opérations et qu'un utilisateur non-Qt-saavy doit interagir avec cette boucle d'événement, l'écriture de l'emplacement pour gérer des appels réguliers à partir d'un autre thread peut simplifier les choses pour les autres utilisateurs.
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