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 :

Arrêter, mettre en pause et recommencer [QThread]


Sujet :

Multithreading

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2010
    Messages
    248
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 248
    Par défaut Arrêter, mettre en pause et recommencer
    Bonjour à tous,

    Je débute actuellement dans l'utilisation des threads avec Qt (et en général).
    J'ai donc mis en place un petit projet de test en m'inspirant des divers recommandations de la doc Qt et des forums.

    J'essaye d'utiliser la solution utilisant QObject::moveToThread() plutôt que d'hériter de QThread. Cette dernière solution ne permettant pas d'utiliser les signaux/slots .

    J'ai donc implémenté une class "Worker" dont le rôle sera de traiter des données. J'ai ajouter deux slots cancel() et togglePause(), qui je connect directement au signal clicked() des mes boutons (ui->m_btStop et ui->m_btPause).
    Le problème étant que ces deux slots ne sont jamais appelé, et je ne comprend pas pourquoi. Si quelqu'un peux m'éclairer, ca serait vraiment sympa.

    Voici mon code :
    worker.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
    41
    42
    43
    44
    45
    #ifndef WORKER_H
    #define WORKER_H
     
    #include <QObject>
    #include <QMutex>
    #include <QWaitCondition>
     
    #include "data.h"
     
    class Worker : public QObject
    {
        Q_OBJECT
    public:
        explicit Worker(QObject *parent = 0);
     
    signals:
        void maxProgressRangeChanged(int maxRange);
        void progressValueChanged(int value);
        void progressTextChanged(const QString &text);
        void finished();
     
        void resultReady(const Data &result);
     
    public slots:
     
        void cancel();
        void togglePaused();
     
        void startWork(const QStringList &list);
     
    public:
        bool isPaused();
        bool isRunning();
     
    private:
        QMutex m_sync;
        QWaitCondition m_pauseCond;
        QWaitCondition m_1secCond;
        volatile bool m_pause;
        volatile bool m_stop;
        bool m_isRunning;
     
    };
     
    #endif // WORKER_H
    worker.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
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    #include <QStringList>
    #include <QTime>
     
    #include "worker.h"
     
    Worker::Worker(QObject *parent) :
        QObject(parent)
    {
        m_pause = false;
        m_stop = false;
        m_isRunning = false;
    }
     
    //Never called - Why ? 
    void Worker::cancel()
    {
        m_sync.lock();
        m_stop = true;
    	m_sync.unlock();
    }
     
    //Never called - Why ? 
    void Worker::togglePaused()
    {
        m_sync.lock();
        m_pause = !m_pause;
        if(!m_pause) m_pauseCond.wakeAll();
        m_sync.unlock();
     
    }
     
    void Worker::startWork(const QStringList &list)
    {
     
        m_sync.lock();
        m_pause = false;
        m_stop = false;
        m_isRunning = true;
        m_sync.unlock();
        int counter = 0;
     
        QStringList internalList = list;
     
        emit maxProgressRangeChanged(list.size());
        emit progressValueChanged(0);
        emit progressTextChanged(tr("Starting...."));
     
        foreach(QString element, internalList){
            //Check pause state
            //=================
            m_sync.lock();
            if(m_pause)
                m_pauseCond.wait(&m_sync); // in this place, your thread will stop to execute until someone calls resume
            if(m_stop) {
                m_isRunning = false;
                m_sync.unlock();
                emit finished();
                break;
            }
            m_sync.unlock();
     
            //Data process
            //=================
            emit progressValueChanged(++counter);
            emit progressTextChanged(tr("Processing \"%1\"").arg(element));
     
            Data data(element.toUpper(), counter);
            //Wait 200 ms to make the process long (this is a test),
            //this can simulate a slow communication, waiting on a hardware, etc..
    		QTime t;
    		t.start();
            while(t.elapsed() < 200){}
     
            //send the data result
            emit resultReady(data);
        }
        m_sync.lock();
        m_pause = false;
        m_stop = false;
        m_isRunning = false;
        m_sync.unlock();
        emit finished();
    }
     
    bool Worker::isPaused()
    {
        m_sync.lock();
        bool res = m_pause;
        m_sync.unlock();
        return res;
     
    }
     
    bool Worker::isRunning()
    {
        m_sync.lock();
        bool res = m_isRunning;
        m_sync.unlock();
        return res;
    }
    MainWindow.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
    41
    42
    43
    44
    45
    46
    47
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
     
    #include <QMainWindow>
     
    #include "data.h"
     
    class Worker;
    class QThread;
     
     
    namespace Ui {
    class MainWindow;
    }
     
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
     
    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
     
    public slots:
        void onBtStart();
        void onBtPause();
        void onBtStop();
     
        void dataReady(const Data &data);
        void onProcessFinished();
     
        void threadStarted();
        void threadFinished();
     
    signals:
        void startWork(const QStringList &list);
     
        void pause();
        void stop();
     
    private:
        Ui::MainWindow *ui;
        Worker *worker;
        QThread *thread;
    };
     
    #endif // MAINWINDOW_H
    MainWindow.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
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    #include <QThread>
     
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
     
    #include "worker.h"
     
     
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
     
        qRegisterMetaType<Data>();
     
        worker = new Worker;
        thread = new QThread(this);
     
        ui->progressBar->setVisible(false);
        ui->laProcess->setVisible(false);
        ui->btPause->setEnabled(false);
        ui->btStop->setEnabled(false);
        ui->btStart->setEnabled(true);
     
        connect(worker, SIGNAL(maxProgressRangeChanged(int)), ui->progressBar, SLOT(setMaximum(int)));
        connect(worker, SIGNAL(progressTextChanged(QString)), ui->laProcess, SLOT(setText(QString)));
        connect(worker, SIGNAL(progressValueChanged(int)), ui->progressBar, SLOT(setValue(int)));
        connect(worker, SIGNAL(resultReady(Data)), this, SLOT(dataReady(Data)));
        connect(worker, SIGNAL(finished()), this, SLOT(onProcessFinished()));
     
        connect(thread, SIGNAL(started()), this, SLOT(threadStarted()));
        connect(thread, SIGNAL(finished()), this, SLOT(threadFinished()));
     
        connect(ui->btPause, SIGNAL(clicked()), worker, SLOT(togglePaused()), Qt::QueuedConnection);
        connect(ui->btStop, SIGNAL(clicked()), worker, SLOT(cancel()), Qt::QueuedConnection);
        connect(ui->btStart, SIGNAL(clicked()), this, SLOT(onBtStart()));
     
        connect(this, SIGNAL(startWork(QStringList)), worker, SLOT(startWork(QStringList)));
     
        worker->moveToThread(thread);
    }
     
    MainWindow::~MainWindow()
    {	
        emit stop();
        thread->exit();
        thread->wait();
        delete thread;
     
        delete ui;
    }
     
    void MainWindow::onBtStart()
    {
       QStringList list;
     
       thread->start();
     
       ui->progressBar->setVisible(true);
       ui->laProcess->setVisible(true);
     
       //Set random data 
       list << "Nullam" << "libero" << "at" << "sapien" << "vestibulum";
     
       //Start processing of the data
       emit startWork(list);
       ui->btPause->setEnabled(true);
       ui->btStop->setEnabled(true);
       ui->btStart->setEnabled(false);
    }
     
    void MainWindow::onBtPause()
    {
        emit pause();
    }
     
    void MainWindow::onBtStop()
    {
        emit stop();
    }
     
    void MainWindow::dataReady(const Data &data)
    {
        Data internalData(data);
        ui->edLog->append(QString("Data processed : %1 --> %2")
                          .arg(internalData.value(), 8, 10, QChar('0'))
                          .arg(internalData.name()));
    }
     
    void MainWindow::onProcessFinished()
    {
        ui->edLog->append("All data has been processed ! ");
        ui->progressBar->setVisible(false);
        ui->laProcess->setVisible(false);
        ui->btPause->setEnabled(false);
        ui->btStop->setEnabled(false);
        ui->btStart->setEnabled(true);
    }
     
    void MainWindow::threadStarted()
    {
        ui->edLog->append("Thread started ! ");
    }
     
    void MainWindow::threadFinished()
    {
        ui->edLog->append("Thread finished ! ");
    }
    Je n'ai pas ajouté l'image de la GUI, mais c'est très simple,
    - 3 boutons start, pause, stop.
    - 1 progressBar
    - 1 label (traitement en cours)
    - 1 TextEdit pour afficher le log de ce qui se passe.

    Je sais qu'il existe pas mal de topic sur le sujet, mais je n'ai rien trouvé répondant à mon problème ( ou peut être n'ai-je pas compris tout simplement), de plus, ceci étant ma première utilisation des thread avec Qt, j'aimerais vraiment être sûre d'avoir bien compris le sujet pour partir sur de bonne bases.

    Merci d'avance

  2. #2
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 119
    Billets dans le blog
    148
    Par défaut
    Bonjour,

    J'ai un peu de mal à comprendre pourquoi Worker n'hérite pas de QThread ? Cela ne simplifierai pas la tache ?
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  3. #3
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2010
    Messages
    248
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 248
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    J'ai un peu de mal à comprendre pourquoi Worker n'hérite pas de QThread ? Cela ne simplifierai pas la tache ?
    Bonjour,

    En fait d'après ce que j'ai compris dans la doc Qt et les forums, si j'hérite de QThread, seul la fonction run() sera exécutée dans un thread différent, et visiblement les signaux et slots ne fonctionneront pas. Dans tous les cas la doc de Qt indique clairement qu'il est déconseillé de définir des nouveau slots dans une classe héritée de QThread.

    En utilisant moveToThread(), l'objet complet est déplacé dans un autre thread. Cette la méthode recommandé par la doc de Qt.

    Une description plus détaillé est disponible ici

    J'éspère avoir répondu à votre question

  4. #4
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 119
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 119
    Billets dans le blog
    148
    Par défaut
    Quelque chose me dit que c'est lié au Qt::QueuedConnection. Mais je ne crois pas que ce soit tout, mais le laisser par défault mais un paramètre automatique, qui résoud généralement bien les connexions. Ensuite, il faut voir sur la console ne retourne pas des warnings sur les connexions.

    (Et oui, je m'étais basé sur le modèle 4.X pour ma réponse précédente).
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  5. #5
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2010
    Messages
    248
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 248
    Par défaut QueuedConnection
    Citation Envoyé par LittleWhite Voir le message
    Quelque chose me dit que c'est lié au Qt::QueuedConnection. Mais je ne crois pas que ce soit tout, mais le laisser par défault mais un paramètre automatique, qui résoud généralement bien les connexions. Ensuite, il faut voir sur la console ne retourne pas des warnings sur les connexions.

    (Et oui, je m'étais basé sur le modèle 4.X pour ma réponse précédente).
    En fait j'ai essayé les deux possibilités, en forçant Qt::QueuedConnection ou non (valeur par défaut), mais sans succès dans un cas comme dans l'autre. Désolé j'ai oublié de mentionné cela et de supprimer les "Qt::QueuedConnection" dans mon code.

    Ce que je ne comprend pas c'est que les signaux provenant de worker
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    connect(worker, SIGNAL(resultReady(Data)), this, SLOT(dataReady(Data)));
        connect(worker, SIGNAL(finished()), this, SLOT(onProcessFinished()));
    fonctionnent très bien, alors que dans l'autre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    connect(ui->btPause, SIGNAL(clicked()), worker, SLOT(togglePaused()));
        connect(ui->btStop, SIGNAL(clicked()), worker, SLOT(cancel()));
    sens cela ne fonctionne pas.

    Edit : Concernant les warnings, je n'en ai aucun s'affichant dans la console.

  6. #6
    Membre très actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2010
    Messages
    248
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : Suisse

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

    Informations forums :
    Inscription : Mai 2010
    Messages : 248
    Par défaut processEvents
    Je crois que je commence à comprendre. En modifiant l'appel de mes signaux ( j'ai passé par un slot transitoire dans MainWindow, j'avais donc le signal clicked() de mes boutons qui appelait un slot dans MainWindow, qui lui appelait le slot dans worker). A ce moment la j'ai remarqué que mes slots Worker::togglePause() et Worker::cancel() était bien appelé cette fois, mais seulement à la fin du traitement.

    J'ai ajouté
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    QCoreApplication::processEvents();
    à la fin du traitement de ma fonction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Worker::startWork(QStringList list){
            ...
            //send the data result
            emit resultReady(data);
     
    		//Process event in the queue
    		QCoreApplication::processEvents();
        }
    et à ce moment la mes slots Worker::cancel() et Worker::togglePause() sont bien appelés au bon moment. Mais j'ai encore dû modifier ma condition d'attente dans la fonction Worker::startWork afin que la ligne m_pauseCond.wait(&m_sync) ne soit pas blockante, sinon c'est le deadlock assuré. La modifs :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if(m_pause){ // in this place, your thread will stop to execute until someone calls resume
         while(!m_pauseCond.wait(&m_sync, 50)) 
                QCoreApplication::processEvents();
    }
    Cette solution ne me paraît pas idéale , la boucle while va certainement utiliser beaucoup de charge CPU inutilement. Néanmoins je ne vois pas d'autre solutions pour l'instant.

    Merci LittleWhite pour vos réponses rapides ainsi que votre aide qui m'as permis de m'aiguiller et me poser les bonnes questions.

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

Discussions similaires

  1. Mettre une pause dans un programme
    Par PNL dans le forum Général Java
    Réponses: 12
    Dernier message: 28/01/2016, 00h54
  2. Réponses: 6
    Dernier message: 15/05/2011, 18h50
  3. [VB6] Mettre en pause l'execution du code
    Par ironik dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 19/05/2006, 10h56
  4. [FLASH MX2004] Comment mettre une pause dans un script
    Par vbcasimir dans le forum Flash
    Réponses: 3
    Dernier message: 16/02/2006, 09h47
  5. Mettre en pause le Timer !
    Par NaDiA_SoFt dans le forum C++Builder
    Réponses: 14
    Dernier message: 12/09/2003, 21h32

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