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 :

Un moveToThread() rebelle [QThread]


Sujet :

Multithreading

  1. #1
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut Un moveToThread() rebelle
    Bonsoir,

    J'ai créé un petit projet avec Qt Creator pour apprendre à manipuler les threads avec QThread. Ce projet qui n'a qu'un but ludique consiste à créer une sorte de métronome dans un thread différent du thread principal. Jusque là, rien d'anormal.

    Voici le fichier 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
    #include <QApplication>
     
    #include "mygui.h"
    #include "myclockwork.h"
     
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
     
        MyGui gui;
        MyClockwork clockwork;
        app.connect(&clockwork, SIGNAL(tick()), &gui, SLOT(tick()));
        app.connect(&app, SIGNAL(aboutToQuit()), &clockwork, SLOT(stop()));
     
        gui.show();
        clockwork.start();
     
        return app.exec();
    }
    Comme on peut le voir, le métronome (classe MyClockwork) possède deux slots publics, start() et stop(), ainsi qu'un signal tick(). Par ailleurs, le slot stop() est appelé quand l'application est sur le point de quitter; le problème réside là.
    Voici les fichiers liés à la classe du métronome:
    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
    #ifndef MYCLOCKWORK_H
    #define MYCLOCKWORK_H
     
    #include <QObject>
     
    #include <QApplication>
    #include <QThread>
    #include <QTimer>
     
    class MyClockwork : public QObject
    {
        Q_OBJECT
     
        public:
            MyClockwork(QObject* parent = 0);
            ~MyClockwork();
     
        public slots:
            void start();
            void stop();
     
        signals:
            void tick();
     
        private:
            QThread* m_thread;
            QTimer* m_timer;
     
        private slots:
            void timerFired();
    };
     
    #endif // MYCLOCKWORK_H
    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
    #include "myclockwork.h"
     
    MyClockwork::MyClockwork(QObject* parent) :
        QObject     (parent)        ,
        m_thread    (new QThread)   ,
        m_timer     (new QTimer)
    {
        m_timer->setSingleShot(false);
    }
     
    MyClockwork::~MyClockwork()
    {
        delete m_timer;
        delete m_thread;
    }
     
    void MyClockwork::start()
    {
        m_timer->setInterval(1000);
        connect(m_timer, SIGNAL(timeout()), this, SLOT(timerFired()), Qt::DirectConnection);
        m_timer->start();
        m_timer->moveToThread(m_thread);
        m_thread->start();
    }
     
    void MyClockwork::stop()
    {
        if(m_thread->isRunning())
        {
            m_timer->moveToThread(QApplication::instance()->thread());
            m_thread->quit();
        }
        m_timer->stop();
        disconnect(m_timer, SIGNAL(timeout()), this, SLOT(timerFired()));
    }
     
    void MyClockwork::timerFired()
    {
        emit tick();
    }
    Le problème est que, lorsque je renvoie le minuteur m_timer dans le thread principal via la commande m_timer->moveToThread(QApplication::instance()->thread()), je reçois l'erreur suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    QObject::moveToThread: Current thread (0x3779a8) is not the object's thread (0x7262a0).
    Cannot move to target thread (0x3779a8)
    En gros, l'application ne parvient pas à mettre l'objet QTimer dans le thread-cible.
    Pourquoi? Je ne sais pas; c'est pourquoi je sollicite votre aide.

    Merci d'avance.

    Adishatz!

    PS: Quand l'application est terminée, j'obtiens aussi cette erreur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QObject::killTimer: timers cannot be stopped from another thread
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  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.
    C'est normal. Un QOBject (ici ton timer) est associé à un QThread.

    Au debut, le timer est associé au thread principale.
    Tu appel movetothread depuis le thread principal => le timer est associé à ton thread

    Au stop tu veux changer l'association du timer avec le thread principal => error. Cette fonction ne peut être exécuté que par le thread associé au timer. Tout comme le stop.

    http://doc.qt.digia.com/stable/qobje...l#moveToThread
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Warning: This function is not thread-safe; the current thread must be same as the current thread affinity. In other words, this function can only "push" an object from the current thread to another thread, it cannot "pull" an object from any arbitrary thread to the current thread.
    c'est ce que tu fais

  3. #3
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Je vois.

    J'avais créé cette fonction stop() non seulement pour arrêter proprement mon thread mais aussi pour ne pas avoir l'erreur ci-dessous.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QObject::killTimer: timers cannot be stopped from another thread
    Dois-je créer une classe spéciale pour déplacer le timer de l'intérieur du thread? Dois-je envisager une autre alternative?
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  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 VivienD Voir le message
    Dois-je créer une classe spéciale pour déplacer le timer de l'intérieur du thread? Dois-je envisager une autre alternative?
    Ca depend de ce que tu veux faire...

    Que cherche tu à tester?

  5. #5
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Je veux juste arrêter proprement mon minuteur pour ne plus avoir cette erreur.
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

  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 VivienD Voir le message
    Je veux juste arrêter proprement mon minuteur pour ne plus avoir cette erreur.
    Au plus simple, tu lance un signal qui est connecté au stop du timer.

    Regarde la FAQ :
    http://qt.developpez.com/faq/?page=m...d-appartenance
    http://qt.developpez.com/faq/?page=m...read-connexion

  7. #7
    Membre averti

    Homme Profil pro
    Inscrit en
    Février 2010
    Messages
    243
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Février 2010
    Messages : 243
    Points : 398
    Points
    398
    Par défaut
    Ci-joint ThreadTest_initial.zip, contient le projet initial, copié-collé de ton code.
    J'ai rajouté un LCD dans la gui pour visualisation.
    Tout semble fonctionner, mais on a des vilains warning lorsqu'on quitte :
    QObject::moveToThread: Current thread (0x5cad88) is not the object's thread (0x28cf80).
    Cannot move to target thread (0x5cad88)

    QObject::killTimer: timers cannot be stopped from another thread
    Ce type d'erreur provient du fait qu'on mélange QThread, qui pilote le thread et le code qui tourne dans le thread.
    Pour bien me faire comprendre, ci dessous je crée une classe ThreadRunner qui est l'exécuteur du thread et la classe MyClockWork ne s'occupe plus que du "travail", du code qui tourne DANS le thread. MyClockWork peut être exécutée ou non dans un thread sans rien changer à son code.

    Cette fois-ci, plus aucun warning. Tout est propre.

    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
     
    int main(int argc, char* argv[])
    {
        QApplication app(argc, argv);
     
        MainWindow gui;
     
        ThreadRunner threadRunner(new MyClockwork());
        app.connect(&threadRunner, SIGNAL(taskStarted()), threadRunner.worker(), SLOT(startWork()));
        app.connect(threadRunner.worker(), SIGNAL(tick()), &gui, SLOT(tick()));
     
        // On win close, stop the thread and wait for it to finish before exiting
        app.connect(&gui, SIGNAL(aboutToQuit()), &threadRunner, SLOT(stop()));
        app.connect(&threadRunner, SIGNAL(taskFinished()), &app, SLOT(quit()));
     
        gui.show();
        threadRunner.start();
     
        return app.exec();
    }
    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
     
    MyClockwork::MyClockwork() :
        QObject(0),                             // Makes sure this object has no parent because of moveToThread
        m_timer(new QTimer(this))               // timer is a child of the thread so it moves with MyClockWork
    {
        // Direct connection not necessary, timer and this are in the same thread.
        connect(m_timer, SIGNAL(timeout()), this, SLOT(timerFired()));
    }
     
    MyClockwork::~MyClockwork()
    {
    }
     
    void MyClockwork::startWork()
    {
        // The thread is started, this function runs in the thread
        m_timer->setSingleShot(false);
        m_timer->setInterval(1000);
     
        m_timer->start();
    }
     
    void MyClockwork::stopWork()
    {
        m_timer->stop();
    }
     
    void MyClockwork::timerFired()
    {
        // Still running in the thread
        emit tick();
    }
    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
     
    ThreadRunner::ThreadRunner(QObject *worker, QObject *parent) :
        QObject(parent),
        m_worker(worker),
        m_thread(new QThread(this))
    {
        // Worker cannot have a parent !
        Q_ASSERT(worker->parent() == 00);
        connect(m_thread, SIGNAL(started()), this, SIGNAL(taskStarted()));
        connect(m_thread, SIGNAL(finished()), this, SLOT(threadFinished()));
     
        // Moves worker in the m_thread, all children of the worker are moved as well
        // So worker can be construct as a classic QObject.
        // From now all slots of worker will run in the thread
        worker->moveToThread(m_thread);
    }
     
    void ThreadRunner::start()
    {
        m_thread->start();
    }
     
    void ThreadRunner::stop()
    {
        // Simply stop the eventloop
        m_thread->quit();
    }
     
    void ThreadRunner::threadFinished()
    {
        // Now it is safe to destroy the worker.
        delete m_worker;
        emit taskFinished();
    }
    Fichiers attachés Fichiers attachés

  8. #8
    Membre émérite
    Avatar de VivienD
    Homme Profil pro
    Développeur logiciel
    Inscrit en
    Octobre 2009
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Allemagne

    Informations professionnelles :
    Activité : Développeur logiciel
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2009
    Messages : 523
    Points : 2 278
    Points
    2 278
    Par défaut
    Citation Envoyé par yan Voir le message
    Au plus simple, tu lance un signal qui est connecté au stop du timer.

    Regarde la FAQ :
    http://qt.developpez.com/faq/?page=m...d-appartenance
    http://qt.developpez.com/faq/?page=m...read-connexion
    Citation Envoyé par ness522 Voir le message
    [tout le message]
    Merci à vous deux.
    Je vais étudier le tout à tête reposée.

    Je crois que ce sujet mérite désormais une petite estampille .

    Tschüß!
    De retour, plus sportif mais toujours aussi moche.
    _____________
    Pro: Programmation en C/C++ (embarqué ou non)
    Loisir: Programmation en C++11/14/17 avec la STL ou Qt 5

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

Discussions similaires

  1. Colonne de menu déroulant rebelle
    Par Digiwolf dans le forum Access
    Réponses: 9
    Dernier message: 31/01/2006, 09h51
  2. [MySQL] Apostrophes rebelles !!!
    Par JoN28fr dans le forum PHP & Base de données
    Réponses: 8
    Dernier message: 13/12/2005, 22h57
  3. [VB6] ActiveChart se rebelle!
    Par rupeni dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 26/11/2005, 17h07
  4. [DW MX 2004] Cadre rebel !
    Par Ticoche dans le forum Dreamweaver
    Réponses: 3
    Dernier message: 29/09/2005, 12h54
  5. [SQL SERVER]des doublons rebelles
    Par trotters213 dans le forum Langage SQL
    Réponses: 10
    Dernier message: 25/03/2005, 15h12

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