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 :

Signal et slot dans un cas multithread à 2 voire 3 niveaux


Sujet :

Multithreading

  1. #1
    Membre du Club
    Inscrit en
    Octobre 2007
    Messages
    66
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 66
    Points : 50
    Points
    50
    Par défaut Signal et slot dans un cas multithread à 2 voire 3 niveaux
    Bonjour,

    dans le cadre de mon projet j'ai du séparer les boucles de fonctionnement entre 2 threads principaux (récepteur et de calcul) et le second peut lui aussi créer des threads secondaires.
    Mon problème est d'arriver à envoyer des signaux du thread secondaire au 1er thread principal. Je viens de découvrir que le connect ne suffit pas car il ne semble pas y avoir de lien entre les 2 threads.
    Je pourrai envoyer des signaux au 2nd thread principal qui enverrait alors un signal au 1er thread principal mais je me demande si on ne peut pas faire plus simple.
    J'oubliais de préciser que j'utilise un classe statique pour envoyer mes signaux pour qu'ils soient disponible à tous les niveaux de mes threads (et classes) de mon thread de calcul.

    Je n'ai pas de probleme de connexion car j'ai testé hors du 2 niveaux de thread et tout fonctionne bien.

    Voici un essaie de présentation de mon problème avec du code simple (et certainement des erreurs car c'est vraiment simplifié par rapport à mon programme) mais c'est pour aider peut être à la compréhension :

    Reciever.h - recepteur 1 thread principal
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <QThread>
    #include <QObject>
     
    class Reciever : public QThread
    {
        Q_OBJECT
    public:
        Reciever();
        void inititateProcess();
    public slots:
        void do_result(std::string, double);
    }
    computeSignals.h - pour envoyer les signaux
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    class ComputeSignals : public QObject {
    	  Q_OBJECT
      public :
    	  static ComputeSignals *getComputeSignal();
     
    	  void emitNewResult(std::string name, double value){ emit newResult(name,value);}
     
      signals :
    	  void newResult(std::string name, double value);
      private :
    	  static ComputeSignals *computeSignals;
      };

    ComputeThread.h - thread principal 2 de calcul
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <QThread>
     
    class ComputeThread : public QThread
    {
        Q_OBJECT
    public:
        ComputeThread();
        void initiateProcess();
     
    private: 
    virtual void run(void);
    }
    ComputeThread.cpp - thread principal 2 de calcul
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    ...
    void ComputeThread::run(void){
      ComputeSignals::getComputeSignal()->emitNewResult("test", 1337);
      if(...){
          thread = new computeThread2();
          thread->Start();
      }
    ...
    }
    computeThread2 - thread de calcul secondaire appeler par le thread de calcul principal il doit pouvoir envoyer des signaux au récepteur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <QThread>
     
    class ComputeThread2 : public QThread
    {
        Q_OBJECT
    public:
        ComputeThread2();
        void initiateProcess();
     
    private: 
    virtual void run(void);
    }
    ComputeThread2.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    ComputeThread2::run(void){
    ...
    ComputeSignals::getComputeSignal()->emitNewResult("test2", 111);
    ...
    }
    Et enfin le main
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    int main (int argc, char **argv) {
    Reviever *recieve = new Reciever();
    QObject::connect(ComputeSignals::getComputeSignal(), SIGNAL(newResult(std::string, double)),recieve, SLOT(do_result(std::string, double)));
     
    ComputeThread *t = new ComputeThread();
    t->start();
    reciever->start();
    }

  2. #2
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonjour,

    Je ne sais pas si j'ai très bien saisi la question, mais vous semblez vouloir connecter deux threads par un connect() dont le slot serait traité non pas par le thread émettant le signal mais par le thread possédant le slot.

    Par défaut, les connect() se font en Qt::AutoConnection, ce qui signifie que le choix entre Qt:: DirectConnection et Qt::QueuedConnection est fait automatiquement selon le fait que l'émetteur et le receveur appartiennent à deux threads différents. Dans le cas présent, je vous conseille d'expliciter cela avec Qt::QueuedConnection en dernier paramètre de vos connect() et non Qt::AutoConnection (le paramètre par défaut si non précisé) vu que vous n'avez pas fait de moveToThread() préalable. En Qt::QueuedConnection, quand un thread A va émettre un signal connecté vers le slot d'un thread B, l'appel au slot va être mis en attente de traitement dans l'event loop du thread B.

    Cela mène à parler de l'event loop : quand vous réimplémentez la méthode run() de QThread pour y faire votre boucle infinie, vous bloquez la boucle d'événements du thread concerné, ce qui mènera les connexions Queued à ne pas aboutir, vu qu'il n'y a aucun processEvents() ou autre pour dépiler les appels en attente. À moins que je n'ai pas tout à fait saisi le problème et que seul Reciever nécessite de traiter les slots ? En regardant plus en détail, j'ai l'impression qu'il manque juste les étapes de moveToThread avant le connect() pour permettre à ce dernier de déterminer correctement le type de connexion.

    Bref, désolé pour cette réponse un peu en fouillis, en espérant que ça vous éclaire un minimum quand même,

    Bonne journée,
    Louis

  3. #3
    Membre du Club
    Inscrit en
    Octobre 2007
    Messages
    66
    Détails du profil
    Informations forums :
    Inscription : Octobre 2007
    Messages : 66
    Points : 50
    Points
    50
    Par défaut
    Bonjour Amnell,

    c'était effectivement le problème des évènements bloqués qui me posait problème... j'ai résolu dans un premier temps le problème en lancant ma méthode run (renommée) via un QTimer, mais ça ne fonctionne pas car je dois pouvoir spécifier quand la boucle de l'un de mes threads principals.

    Je recherche de la documentation pour remettre en fonctionnement les events tout en redéfinissant le thread mais je n'ai pas trouvé grand chose malheureusement. Si tu as un lien cela m'intéresse. Sinon je vais utiliser QTimer avec une boucle de temps très courte et faire des tests.

    Merci en tout cas pour ton aide précieuse.

  4. #4
    Rédacteur
    Avatar de Amnell
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2009
    Messages
    1 840
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2009
    Messages : 1 840
    Points : 5 545
    Points
    5 545
    Par défaut
    Bonsoir,

    En fait, je pense qu'il faut considérer la méthode run() par défaut comme étant une boucle infinie grossièrement de cette forme :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Forever
    {
        Si des événements sont en attente
            Traitement des événements
    }
    Parmi les événements se situent notamment les appels en QueuedConnection. Mettons alors que le thread reçoit un slot menant à une boucle infinie (votre méthode run() renommée), cela donnera ce scénario :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Forever
    {
        Si des événements sont en attente
            Traitement des événements
            => Appel à votre méthode renommée
            => Boucle infinie
    }
    Par conséquent, la procession va être bloquée par votre nouvelle boucle infinie, et on va revenir au problème initial, c'est-à-dire à l'absence de traitement des événements en attente. La stratégie à adopter est alors le fait de diviser votre boucle infinie en plusieurs étapes (une étape pouvant par exemple être un tour de la boucle infinie si ça n'est pas trop long par rapport à vos contraintes de développement), par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Constructeur
    {
        connect(this, SIGNAL(workRequest()), this, SLOT(doWork()), Qt::QueuedConnection);
        connect(this, SIGNAL(started()), this, SLOT(doWork()), Qt::QueuedConnection); // démarrage automatique du traitement lors de l'appel au start() du thread
    }
     
    doWork
    {
        Traitement d''une étape de calcul
        Si le thread doit continuer de travailler
            emit workRequest();
    }
    Avec ce système, votre méthode doWork() n'étant pas une boucle infinie, elle ne va pas bloquer le traitement des événements. L'intérêt d'appeler le signal workRequest() en QueuedConnection va justement permettre de passer par le système d'événements pour demander un rappel de votre fonction doWork() sans avoir à passer par un Timer qui handicaperait de manière significative les performances. Notez qu'en DirectConnection au lieu d'en QueuedConnection, cela reviendrait à non pas écrire "emit workRequest()" mais plutôt "doWork()" et de faire une récursion infinie, avec le stack overflow qui va avec en plus du blocage de la boucle d'événements.

    Une source qui pourra vous intéresser est la suivante (cela traite notamment du moveToThread et cela vient en complément de mon explication) : http://qt-labs.developpez.com/thread...-movetothread/

    Bonne soirée à vous,
    Louis

Discussions similaires

  1. UML : Qui s'en sert ? Pourquoi ? Dans quels cas ? Où ?
    Par Matthieu Brucher dans le forum UML
    Réponses: 83
    Dernier message: 10/06/2013, 17h13
  2. Comment utiliser le multithread dans mon cas?
    Par Vanito dans le forum Général Dotnet
    Réponses: 12
    Dernier message: 04/10/2012, 09h39
  3. [Débutant][JList] Comment ça marche dans mon cas ?
    Par gcore dans le forum Composants
    Réponses: 31
    Dernier message: 28/06/2004, 11h45
  4. Quel type de BDD dans mon cas
    Par zoubidaman dans le forum Décisions SGBD
    Réponses: 4
    Dernier message: 10/06/2004, 19h00
  5. [corba] débutant : dans quels cas l'utiliser
    Par jmturc dans le forum CORBA
    Réponses: 2
    Dernier message: 10/10/2002, 09h58

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