Comment émettre un signal cross-thread dans Qt?
-
10-07-2019 - |
Question
de Qt indique que les signaux et les emplacements peuvent être direct
, en file d'attente
et auto
.
Il a également indiqué que si l'objet possédant le logement 'vit' dans un thread différent de celui possédant le signal, émettre un tel signal ressemblerait à l'envoi d'un message - le signal émis sera renvoyé instantanément et la méthode slot sera appelée dans la boucle d'événements du thread cible. .
Malheureusement, la documentation ne spécifie pas que "vies" signifie et qu'aucun exemple n'est disponible. J'ai essayé le code suivant:
main.h:
class CThread1 : public QThread
{
Q_OBJECT
public:
void run( void )
{
msleep( 200 );
std::cout << "thread 1 started" << std::endl;
MySignal();
exec();
}
signals:
void MySignal( void );
};
class CThread2 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 2 started" << std::endl;
exec();
}
public slots:
void MySlot( void )
{
std::cout << "slot called" << std::endl;
}
};
main.cpp:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThread1 oThread1;
CThread2 oThread2;
QObject::connect( & oThread1, SIGNAL( MySignal() ),
& oThread2, SLOT( MySlot() ) );
oThread1.start();
oThread2.start();
oThread1.wait();
oThread2.wait();
return a.exec();
}
La sortie est:
thread 2 started
thread 1 started
MySlot ()
n'est jamais appelé :(. Qu'est-ce que je fais mal?
La solution
Votre code pose quelques problèmes:
- comme dit par Evan, le mot clé emit est manquant
- tous vos objets vivent dans le fil principal, seul le code des méthodes d'exécution vivent dans d'autres threads, ce qui signifie que l'emplacement MySlot serait appelé dans le fil principal et je ne suis pas sûr que ce soit ce que vous voulez
- votre emplacement ne sera jamais appelé car la boucle d'événement principale ne sera jamais lancée: vos deux appels à wait () expireront seulement après un temps très long (et vous tuez probablement votre application avant qu'elle ne survienne) et je Ne pensez pas que ce soit ce que vous voulez non plus, de toute façon ils n’ont vraiment aucune utilité dans votre code.
Ce code fonctionnera probablement (bien que je ne l’aie pas testé) et je pense qu’il fait ce que vous voulez qu'il fasse:
class MyObject : public QObject
{
Q_OBJECT
public slots:
void MySlot( void )
{
std::cout << "slot called" << std::endl;
}
};
class CThread1 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 1 started" << std::endl;
int i = 0;
while(1)
{
msleep( 200 );
i++;
if(i==1000)
emit MySignal();
}
}
signals:
void MySignal( void );
};
class CThread2 : public QThread
{
Q_OBJECT
public:
void run( void )
{
std::cout << "thread 2 started" << std::endl;
exec();
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
CThread1 oThread1;
CThread2 oThread2;
MyObject myObject;
QObject::connect( & oThread1, SIGNAL( MySignal() ),
& myObject, SLOT( MySlot() ) );
oThread2.start();
myObject.moveToThread(&oThread2)
oThread1.start();
return a.exec();
}
Maintenant, MyObject vivra dans thread2 (grâce à moveToThread).
MySignal devrait être envoyé à partir du thread1 (bien que je ne sois pas sûr, il pourrait être envoyé à partir du thread principal, cela n'a pas vraiment d'importance).
Aucune boucle d'événement n'est nécessaire dans thread1, car l'émission d'un signal ne nécessite pas de boucle d'événement. Une boucle d'événement est nécessaire dans thread2 (lanchée par exec ()) pour recevoir le signal.
MySlot sera appelé dans thread2.
Autres conseils
Ne sous-classe pas QThread pour Qt 4.4 +
Bien que la réponse d'Aiua soit bonne, je voudrais signaler quelques problèmes avec QThread et Qt 4.6 ou 4.7.
Cet article le résume: http: / /blog.qt.io/blog/2010/06/17/youre-doing-it-wrong/
Manque de documentation de la part de Qt
Malheureusement, le problème provient d'un manque de mises à jour de la documentation. Avant Qt 4.4, QThread n’avait aucune implémentation run () par défaut, ce qui signifiait que vous deviez sous-classer QThread pour pouvoir l’utiliser.
Si vous utilisez Qt 4.6 ou 4.7, alors vous devriez certainement pas la sous-classe QThread.
Utilisez moveToThread
La clé pour obtenir des emplacements à exécuter dans un thread de travail consiste à utiliser la méthode moveToThread comme l'a souligné Aiua.
vous devriez émettre le signal pour démarrer votre fonction de fil comme
emit operateCut(examId,examName_examTemplate[examName].studentIdRec,examName_examTemplate[examName].choiceRecA,examName_examTemplate[examName].choiceRecB,examName_examTemplate[examName].objectRecA,examName_examTemplate[examName].objectRecB);
vous pouvez ajouter plus d'un argument dans ce signal