Boucles d'événements et traitement des emplacements de signaux lors de l'utilisation du multithreading dans Qt
J'ai eu quelques problèmes avec l'aide de QThreads
qui m'a fait découvrir différentes combinaisons avant, j'ai trouvé le droit. Cependant je n'ai toujours pas bien comprendre ce qui se passe vraiment dans les quatre cas illustré ci-dessous lorsqu'il s'agit d'une boucle d'événement et signal-slot de traitement.
J'ai ajouté quelques commentaires à la section de SORTIE, mais comme vous pouvez le voir je ne suis pas sûr si mes hypothèses sur les causes de comportements observés sont corrects. Aussi je ne suis pas sûr si case 3
est quelque chose qui pourrait être utilisée dans le code réel. Voici mon code de test (uniquement le main.cpp
diffère pour chaque cas):
travailleur.h:
#include <QObject>
#include <QDebug>
#include <QThread>
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0) { this->isRunning_ = false;}
bool isRunning() const { return isRunning_; }
signals:
void processingFinished();
void inProgress();
public slots:
void process()
{
this->isRunning_ = true;
qDebug() << this << "processing started";
for (int i = 0; i < 5; i++)
{
QThread::usleep(1000);
emit this->inProgress();
}
qDebug() << this << "processing finished";
this->isRunning_ = false;
emit this->processingFinished();
}
private:
bool isRunning_;
};
workermanager.h:
#include "worker.h"
class WorkerManager : public QObject
{
Q_OBJECT
public:
explicit WorkerManager(QObject *parent = 0) :
QObject(parent) {}
public slots:
void process()
{
QThread *thread = new QThread();
Worker *worker = new Worker();
connect(thread,SIGNAL(started()),worker,SLOT(process()));
connect(worker,SIGNAL(processingFinished()),this,SLOT(slot1()));
connect(worker,SIGNAL(inProgress()),this,SLOT(slot2()));
worker->moveToThread(thread);
qDebug() << "starting";
thread->start();
QThread::usleep(500);
while(worker->isRunning()) { }
qDebug() << "finished";
}
void slot1() { qDebug() << "slot1"; }
void slot2() { qDebug() << "slot2"; }
};
main.cpp (cas 1 - pas de thread séparé pour
workerManager
):
#include <QCoreApplication>
#include "workermanager.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerManager* workerManager = new WorkerManager;
workerManager->process();
qDebug() << "end";
return a.exec();
}
De SORTIE - deux
slot1
etslot2
appelé àa.exec()
(??? - à l'aide de boucle principale?):
starting
Worker(0x112db20) processing started
Worker(0x112db20) processing finished
finished
end
slot2
slot2
slot2
slot2
slot2
slot1
main.cpp (cas 2 -
workerManager
déplacé vers thread séparé, mais thread a pas démarré):
#include <QCoreApplication>
#include "workermanager.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerManager* workerManager = new WorkerManager;
QThread *thread = new QThread();
workerManager->moveToThread(thread);
workerManager->process();
qDebug() << "end";
return a.exec();
}
De SORTIE ni
slot1
nislot2
a été appelé - (??? boucle d'événement associé avec thread reçoit des signaux, mais depuis le thread n'a pas commencé à fentes ne sont pas appelés?):
starting
Worker(0x112db20) processing started
Worker(0x112db20) processing finished
finished
end
main.cpp (cas 3 -
workerManager
déménagé à autre thread, le thread a commencé, maisworkerManager::process()
appelé viaworkerManager->process()
):
#include <QCoreApplication>
#include "workermanager.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerManager* workerManager = new WorkerManager;
QThread *thread = new QThread();
workerManager->moveToThread(thread);
thread->start();
workerManager->process();
qDebug() << "end";
return a.exec();
}
De SORTIE -
slot2
appelé alors queWorker
encore de l'exécution de sonprocess()
(???):
starting
Worker(0x197bb20) processing started
slot2
slot2
slot2
slot2
Worker(0x197bb20) processing finished
finished
end
slot2
slot1
main.cpp (cas 4 -
workerManager
déménagé à autre thread, le thread a commencé, maisworkerManager::process()
appelé à l'aidestarted()
signal dethread
):
#include <QCoreApplication>
#include "workermanager.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
WorkerManager* workerManager = new WorkerManager;
QThread *thread = new QThread();
workerManager->moveToThread(thread);
QObject::connect(thread,SIGNAL(started()),workerManager,SLOT(process()));
thread->start();
qDebug() << "end";
return a.exec();
}
De SORTIE - tous les événements traités après avoir atteint
a.exec()
(???):
end
starting
Worker(0x7f1d700013d0) processing started
Worker(0x7f1d700013d0) processing finished
finished
slot2
slot2
slot2
slot2
slot2
slot1
Merci pour les précisions.
source d'informationauteur Moomin | 2015-01-20
Vous devez vous connecter pour publier un commentaire.
Tous les résultats que vous avez obtenu sont parfaitement correctes. Je vais essayer de vous expliquer comment cela fonctionne.
Une boucle est une boucle interne dans Qt code qui traite les événements système et utilisateur. Boucle d'événement de thread principal est démarré lorsque vous appelez
a.exec()
. Boucle d'événement d'un autre thread est démarré par défaut de mise en œuvre deQThread::run
.Lorsque Qt décide qu'il est temps de traitement d'un événement, il exécute son gestionnaire d'événement. Tout gestionnaire d'événement de travail, Qt n'a aucune chance de traiter de tout autre événement (sauf donnée directement par
QApplication::processEvents()
ou d'autres méthodes). Une fois que le gestionnaire d'événement est terminé, le contrôle de flux retourne à la boucle d'événement de Qt et peut exécuter un autre gestionnaire de processus d'un autre événement.Les signaux et les slots ne sont pas les mêmes que les événements et les gestionnaires d'événement de Qt terminologie. Mais les emplacements sont gérés par une boucle d'événement un peu de la même manière. Si vous avez le contrôle de flux dans votre code(comme dans
main
fonction), vous pouvez exécuter n'importe quelle fente immédiatement juste comme toute autre fonction C++. Mais quand Qt n'est qu'il ne peut le faire qu'à partir d'une boucle d'événements. Il convient de noter que les signaux sont toujours envoyés immédiatement, tandis que la fente de l'exécution peut être retardée.Maintenant, nous allons voir ce qui se passe dans chaque cas.
Cas 1
WorkerManager::process
est directement exécutée au démarrage du programme. Nouveau thread est démarré etWorker::process
est immédiatement exécuté dans le nouveau thread.WorkerManager::process
continue l'exécution jusqu'à ce que le Travailleur est fait, le gel de toutes les autres actions (y compris logement de traitement) dans le thread principal. AprèsWorkerManager::process
est terminée, le contrôle de flux va àQApplication::exec
. Qt établit une connexion à l'autre thread, reçoit des messages sur la fente de invokation et appelle tous d'entre eux en conséquence.Cas 2
Qt par défaut exécute les fentes d'un objet dans le thread de cet objet appartient. Thread principal ne va pas s'exécuter fentes de
WorkerManager
car il appartient à un autre thread. Cependant, ce fil n'a jamais commencé. Sa boucle d'événement n'est jamais fini. Invokations deslot1
etslot2
sont laissés à jamais dans Qt de la file d'attente pour vous de commencer la discussion. Triste histoire.Cas 3
Dans ce cas
WorkerManager::process
est exécuté dans le thread principal parce que vous appelez directement depuis le thread principal. Pendant ce temps,WorkerManager
's thread est démarré. Sa boucle d'événement est lancé et l'attente des événements.WorkerManager::process
commenceWorker
's thread et exécuteWorker::exec
.Worker
commence à envoyer des signaux àWorkerManager
.WorkerManager
's thread presque immédiatement commence à s'exécuter fentes appropriées. À ce stade, il semble maladroit queWorkerManager::slot2
etWorkerManager::process
sont exécutées simultanément. Mais il est parfaitement bien, au moins siWorkerManager
est thread-safe. Peu de temps aprèsWorker
est fait,WorkerManager::process
est fini eta.exec()
est exécuté, mais n'a pas beaucoup de processus.Cas 4
Fonction principale, lance
WorkerManager
's thread et passe immédiatement àa.exec()
résultant enend
en première ligne dans la sortie.a.exec()
processus de quelque chose et s'assure de l'exécution du programme mais n'exécute pasWorkerManager
'fentes car il appartient à un autre thread.WorkerManager::process
est exécuté dansWorkerManager
'fil de sa boucle d'événements.Worker
's thread est démarré etWorker::process
commence à envoyer des signaux deWorker
's thread pourWorkerManager
's thread. Malheureusement, ce dernier est occupé à exécuterWorkerManager::process
. LorsqueWorker
est fait,WorkerManager::process
également finitions etWorkerManager
's thread exécute immédiatement tous en file d'attente les logements.Le plus grand problème dans votre code est
usleep
et les boucles infinies. Vous devriez presque jamais utiliser ces lorsque vous travaillez avec Qt. Je comprends qu'un sommeil dansWorker::process
est juste un espace réservé pour certains vrai calcul. Mais vous devez supprimer le sommeil et la boucle infinie deWorkerManager
. UtilisationWorkerManager::slot1
pour détecterWorker
's de la résiliation. Si vous développez une application graphique il n'y aurait pas besoin de se déplacerWorkerManager
à un autre thread. Toutes ses méthodes (sans dormir) sera exécuté vite et de ne pas figer l'interface graphique.