IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Multithreading Discussion :

Exécution en boucle [QThread]


Sujet :

Multithreading

  1. #1
    Invité
    Invité(e)
    Par défaut Exécution en boucle
    Bonjour,

    Je développe actuellement un logiciel de prise de mesure et j'ai un soucis pour l'exécution continue du thread.

    Mon logiciel est construit selon une architecture multi-threadée. Principalement, il dispose d'un thread principal (contenant l'IHM) et d'un thread de mesure.

    Constructeur du thread principal :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Manager::Manager(int argv, char* argc[])
    {
         QApplication app(arv, argc);
         m_mesureInterface = new MeasureInterface();
         m_mainGUI = new ClasseHeritantMainWindow();
     
         QObject::connect(m_mainGUI, SIGNAL(startSignal()), m_mesureInterface, SLOT(start()));
         QObject::connect(m_mainGUI, SIGNAL(stopSignal()), m_mesureInterface, SLOT(stop()));
     
         m_mainGUI->show();
         app.exec();
    Et voici une partie du code de la classe de mesure :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    MeasureInterface::MeasureInterface()
    {
         moveToThread(&m_tread);
         m_thread.start();
         QObject::connect(this, SIGNAL(initiateSignal()), this, SLOT(initiate()));
         QObject::connect(this, SIGNAL(nextStep()), this, SLOT(execute()));
         emit initiateSignal();
    }
     
    void MeasureInterface::start()
    {
         m_continue = true;
         execute();
    }
     
    void MeasureInterface::stop()
    {
         m_continue = false;
    }
     
    void MeasureInterface::execute()
    {
         //   Pilotage de l'appareil pour prise de mesure
     
         emit transmitResult(Result res);
     
         if(m_continue)
              emit nextStep();
    }
    Le but de tout ceci est que le thread de mesure doit tourner en continu de façon indépendante, sans jamais être dérangé par l'exécution des autres threads du logiciel. Ma toute première idée avait été une boucle while dans la fonction execute(), mais bien entendu, ce n'est pas possible puisque cela bloque la boucle évènementielle.

    Une autre version, celle actuellement utilisée, envoie au thread principal un signal de résultat, et dès que ce signal est reçu, le thread principal envoie un signal pour rappeler la fonction execute(). Cela fonctionnait bien, mais me pose un problème : si le thread principal est déjà occupé sur une opération longue, le signal est mis en attente et cela bloque le fonctionnement du thread de mesure.

    J'ai donc tenté la solution du code écrit plus haut. A priori, cela fonctionne, mais lorsque j'essaie de stopper l'exécution (par l'envoi du signal stop), tout plante. Ce fonctionnement partiel me laisse penser que l'idée mais est bonne mais que je m'y prend mal.

    Si vous savez ce qui ne colle pas, ou même si vous avez une autre façon de faire à proposer, cela m'aiderait bien.

    Cordialement.
    Dernière modification par Invité ; 06/03/2013 à 17h19.

  2. #2
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Salut
    Citation Envoyé par Lelfic Voir le message
    Ma toute première idée avait été une boucle while dans la fonction execute(), mais bien entendu, ce n'est pas possible puisque cela bloque la boucle évènementielle.
    Pourquoi es ce que bloquer l'eventloop du thread te derange?

  3. #3
    Invité
    Invité(e)
    Par défaut
    Si je bloque l'eventloop, les signaux ne sont plus reçus par le thread, et je ne peux donc plus interrompre ma boucle while.

  4. #4
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Lelfic Voir le message
    Si je bloque l'eventloop, les signaux ne sont plus reçus par le thread, et je ne peux donc plus interrompre ma boucle while.
    oui et non ^^
    as tu fait un tour dans la FAQ?
    Ce qui faut bien comprendre c'est qui est exécute par qui. Ton thread doit lancer une eventloop unique si un QObject est associé a ton thread.
    Dans ton cas tu ne semble pas avoir besoin de l'eventloop. Il y as une subtilité important a comprendre : un QThread interface un thread mais n'est pas executé dans ce thread. Tu peux donc faire une boucle while(test) dans le run et un slot dans le QTHread qui mettra test faux pour finaliser la boucle while.

    Si tu as besoin des signal/slot dans le thread, il faut mieux créer des QObject et faire des movetothread. Depuis 4.7 ou 4.6 tu n'est pas obligé de reimplementer QThread. Par defaut il lance une eventloop dans un thread.

  5. #5
    Invité
    Invité(e)
    Par défaut
    Nulle part dans mon logiciel je n'hérite QThread, j'utilise toujours, justement, la méthode du moveToThread() indiquée dans le sujet en tête du forum.

    je n'utilise donc pas la fonction run() mais la fonction execute() de mon cru qui est un slot. Je crois que quelque chose m'échappe.

    Mon object MeasureInterface possède un QThread (natif, pas hérité). Le constructeur de mon objet, comme indiqué dans le premier post, apelle moveToThread() qui devrait, si j'a ibien compris, faire en sorte que les slots de l'objet s'exécutent dans ce QThread lorsqu'ils sont appellés par des signaux. Le slot en question, c'est la fonction execute(), qui est appelée une première par un signal venant du thread principal de l'application, et qui ensuite s'appelle elle même par son propre signal. J'ai tout bon jusque là ?

    Ensuite, l'objet comporte un autre slot servant à interrompre l'envoi du signal qui boucle sur execute(). C'est lorsque j'appelle cet autre slot que mon application plante.

  6. #6
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Lelfic Voir le message
    Mon object MeasureInterface possède un QThread (natif, pas hérité). Le constructeur de mon objet, comme indiqué dans le premier post, apelle moveToThread() qui devrait, si j'a ibien compris, faire en sorte que les slots de l'objet s'exécutent dans ce QThread lorsqu'ils sont appellés par des signaux.
    Yep, mais uniquement si le signal est emit par un autre thread. Sinon cela appelle directement le slot(sauf si tu as changé le mode de connection)

    Le slot en question, c'est la fonction execute(), qui est appelée une première par un signal venant du thread principal de l'application, et qui ensuite s'appelle elle même par son propre signal. J'ai tout bon jusque là ?
    Le premier execute sera ok, mais les suivant vont produire un appel récursive a ta fonction => boom tu va exploser la pile d'appelle ^^

    Pour faire un boucle, tu peux aussi te créer un timer avec un timeout de 0 et reimplementer TimerEvent.

    De tête ca donne un truc comme :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
     
    class monObjet : public Object
    {
    Q_OBJECT
    int timerId;
     
    public :
    	monObjet():timerId(-1){}
     
    public slots :
     
    	void demarerMesure()
    	{
    	if(timerId ==-1)
    		timerId = startTimer(0);
    	}
    	void stopperMesure()
    	{
    		if(timerId !=-1)
    		{
    			killtimer(timerId);
    			timerId = -1;
    		}
    	}
     
    protected :
    	 void timerEvent ( QTimerEvent * event )
    	 {
    		 if(event->timerId ()  == timerId)
    		 {
    		 //mesure
    		 }
    	 }
     
    };
    Pour l'intervalle de 0 regarde la doc ici : http://qt-project.org/doc/qt-4.8/qob...tml#startTimer

  7. #7
    Invité
    Invité(e)
    Par défaut
    Je vois le principe de l'idée pour l'exécution en boucle, mais il y a un point un peu flou.

    Cette méthode est à utiliser en complément de moveToThread() ou en remplacement ?

    Car alors, timerEvent() ,n'étant pas un slot, dans quel thread est-il exécuté ?

  8. #8
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    EN complement. COmme le slot, l'event est exécuté dans le thread associé au QObject.

  9. #9
    Invité
    Invité(e)
    Par défaut
    J'envisage autre-chose :

    Intercaler entre les deux threads un autre thread, construit sur le même principe de moveToThread(), dont la seule fonction serait de recevoir et transmettre les signaux entre l'application et le second thread. De la sorte, ce troisième objet pourrait recevoir le signal nextStep() en provenance du second thread et le lui renvoyer.

    Je sais pas si c'est clair, ce que je dis. Cette solution te semble-t-elle viable ?

  10. #10
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    ? je ne comprend l'avantage d'ajouter ce thread :/
    Car, si j'ai bien compris, il ne fera que passer les messages d'un thread à l'autre

  11. #11
    Invité
    Invité(e)
    Par défaut
    Oui, il servirait donc d'intermédiaire pour le signal de bouclage, en assurant que la fonction execute() est toujours appelée par un thread différent du thread opérant.

  12. #12
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Lelfic Voir le message
    Oui, il servirait donc d'intermédiaire pour le signal de bouclage, en assurant que la fonction execute() est toujours appelée par un thread différent du thread opérant.
    Le systeme de signal/Slot ou d'event te le permet déjà.

  13. #13
    Invité
    Invité(e)
    Par défaut
    Je crois qu'on a du mal à se comprendre. Je pose le problème différemment :

    J'ai donc une appli composée d'un thread principal et d'un thread opérant. Le thread principal envoie les ordres "commence" et "arrête" au thread opérant.
    Ce dernier, à partir du moment où il reçoit l'ordre de commencer, exécute une certaine fonction, et ce en boucle, jusqu'à ce qu'il reçoive l'ordre de s'arrêter.

    Comment t'y prendrais-tu pour implémenter cela (sans le timer) ?

  14. #14
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Lelfic Voir le message
    Comment t'y prendrais-tu pour implémenter cela (sans le timer) ?
    Ben j'aurais fait avec le timer ^^

    Je ferais un exemple compilable dans le week end.

  15. #15
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Voici un exemple. Pour tester, il te suffit de copier le code dans un main.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    #include <QtGui>
    #include <QtWidgets> // pour Qt5
    #include <QtCore>
    #include <QApplication>
     
    class Measure : public QObject
    {
        Q_OBJECT
        int timerid;
     
    public :
        Measure():timerid(-1)
        {}
     
     
    protected :
        void RunMeasure()
        {
     
            newMeasure(QString::number(rand()));
     
        }
        void timerEvent(QTimerEvent * ev)
        {
            if(ev->timerId() == timerid)
                RunMeasure();
        }
     
    signals :
        void newMeasure(QString);
     
     
    public slots :
        void start()
        {
            if(timerid == -1)
                timerid =startTimer(0);
        }
     
        void stop()
        {
            if(timerid != -1)
            {
                killTimer(timerid);
                timerid = -1;
            }
        }
    };
     
    #include "main.moc"
     
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QThread thread;
        thread.start();
        Measure mm;
        mm.moveToThread(&thread);
     
        QWidget w;
        {
            QVBoxLayout * layout = new QVBoxLayout(&w);
            QPushButton * start = new QPushButton("Start");
            QPushButton * stop = new QPushButton("Stop");
            QLabel * label = new QLabel;
     
            layout->addWidget(start);
            layout->addWidget(stop);
            layout->addWidget(label);
     
            QObject::connect(start,SIGNAL(clicked()),&mm,SLOT(start()));
            QObject::connect(stop,SIGNAL(clicked()),&mm,SLOT(stop()));
            QObject::connect(&mm,SIGNAL(newMeasure(QString)),label,SLOT(setText(QString)));
     
        }
     
        w.show();
        a.exec();     
        return 0;
    }

  16. #16
    Invité
    Invité(e)
    Par défaut
    Si j'ai bien compris, je dois donc lancer le timer dans la fonction démarrer(). Et dans la fonction timerEvent(), j'appelle ma fonction execute() (pour ne pas avoir à tout remanier). Mais la fonction execute() prend une dizaine de secondes. est-ce que le timer va attendre la fin de son exécution avant de lancer un nouveau tick ? Ou est-ce que je suis obligé de copier le code de la fonction execute() dans timerEvent() ?

  17. #17
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Citation Envoyé par Lelfic Voir le message
    est-ce que le timer va attendre la fin de son exécution avant de lancer un nouveau tick ?
    En faite l’exécution de ta fonction bloque le thread pendant son exécution. Il n'y aura donc pas de nouveau tick avant la fin.

    Lorsque tu va sortir de la fonction timerEvent, le thread va dépiler l'eventloop et donc exécuter les slots appelé(comme le stop). Puis une fois l'eventloop vidé, timerEvent est appellé.

  18. #18
    Invité
    Invité(e)
    Par défaut
    Ok, merci pour ces réponses, je vais essayer ça.

  19. #19
    Invité
    Invité(e)
    Par défaut
    Cela fonctionne très bien, et c'est plus simple à mettre en oeuvre que je l'imaginais. Merci beaucoup.

  20. #20
    yan
    yan est déconnecté
    Rédacteur
    Avatar de yan
    Homme Profil pro
    Ingénieur expert
    Inscrit en
    Mars 2004
    Messages
    10 033
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur expert
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2004
    Messages : 10 033
    Points : 13 968
    Points
    13 968
    Par défaut
    Cool.
    En faite, le plus dure est de bien comprendre comment fonctionnent les eventloop de Qt.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Exécuter une boucle à une fréquence donnée.
    Par xoomed dans le forum VB 6 et antérieur
    Réponses: 12
    Dernier message: 05/02/2011, 02h31
  2. Lenteur d'exécution (grande boucle accédant à une dll)
    Par kattig dans le forum VB 6 et antérieur
    Réponses: 2
    Dernier message: 11/09/2010, 21h33
  3. modifier/choisir ordre d'exécution de boucles
    Par blable dans le forum LabVIEW
    Réponses: 1
    Dernier message: 14/06/2010, 21h52
  4. Exécuter une boucle tant que le programme tourne
    Par maxetx dans le forum Threads & Processus
    Réponses: 4
    Dernier message: 14/04/2009, 12h40
  5. exécution de boucle de macro
    Par magicfrom dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 23/04/2008, 16h46

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo