67 votes

Quelle est la bonne façon d'implémenter un QThread ... (exemple s'il vous plaît ...)

La documentation de Qt pour QThread dit de créer une classe à partir de QThread, et de mettre en œuvre la méthode run.

Ci-dessous est extrait de 4,7 Qthread de la documentation...

Pour créer votre propre fils, sous-classe QThread et réimplémenter run(). Par exemple:

 class MyThread : public QThread
 {
 public:
     void run();
 };

 void MyThread::run()
 {
     QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);
     exec();
 }

Ainsi, dans chaque thread, j'ai créé, je l'ai fait juste que, et pour la plupart des choses, il fonctionne très bien (je n'ai pas l'œuvre moveToThread(ce) dans l'un de mes objets et il fonctionne très bien).

J'ai frappé un hic, la semaine dernière (a réussi à passer au travers, en travaillant autour de l'endroit où j'ai créé mes objets) et trouvé le blog suivant. Ici est dit essentiellement que les sous-classement QThread n'est vraiment pas la bonne façon de le faire (et que la documentation est incorrect).

Ce qui est à venir à partir d'un Qt developer donc, à première vue, j'ai été intéressé et à la réflexion, d'accord avec lui. Suivant OO principes, vous vous intéressez uniquement à la sous-classe d'une classe pour améliorer encore plus la classe... de ne pas simplement utiliser les classes de méthodes directement... c'est pourquoi vous instanciez...

Disons que j'ai voulu déplacer une coutume classe QObject à un fil... quelle serait la "bonne" façon de faire? Dans ce blog, il dit " il a un exemple quelque part... mais si quelqu'un pourrait-il m'expliquer ce serait grandement apprécié!

Mise à jour:

Depuis que cette question est tellement attention, voici un copier-coller de l'4.8 de la documentation, à la "bonne" façon de mettre en œuvre un QThread.

class Worker : public QObject
 {
     Q_OBJECT
     QThread workerThread;

 public slots:
     void doWork(const QString &parameter) {
         // ...
         emit resultReady(result);
     }

 signals:
     void resultReady(const QString &result);
 };

 class Controller : public QObject
 {
     Q_OBJECT
     QThread workerThread;
 public:
     Controller() {
         Worker *worker = new Worker;
         worker->moveToThread(&workerThread);
         connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
         connect(this, SIGNAL(operate(QString)), worker, SLOT(doWork(QString)));
         connect(worker, SIGNAL(resultReady(QString)), this, SLOT(handleResults(QString)));
         workerThread.start();
     }
     ~Controller() {
         workerThread.quit();
         workerThread.wait();
     }
 public slots:
     void handleResults(const QString &);
 signals:
     void operate(const QString &);
 };

Je crois encore qu'il est intéressant de remarquer qu'elles comprennent un supplément de Worker::workerThread membre qui est inutile et n'est jamais utilisé dans leur exemple. Supprimer cette pièce et c'est un bon exemple de la façon de faire de thread dans Qt.

31voto

Arnold Spence Points 12759

La seule chose que je peux penser à ajouter, c'est encore l'état qui QObjects ont une affinité avec un seul thread. C'est généralement le thread qui crée l' QObject. Donc, si vous créez un QObject dans le thread principal de l'application et que vous souhaitez utiliser dans un autre thread, vous devez utiliser moveToThread() pour changer l'affinité.

Cela évite d'avoir à la sous-classe QThread et la création de vos objets dans l' run() méthode, donc garder des choses bien encapsulé.

Ce billet de blog n'inclure un lien vers un exemple. Il est assez court mais il montre que l'idée de base. Créez votre QObjects, connecter vos signaux, créer votre QThread, déplacez votre QObjects de la QThread et démarrer le thread. Le rapport signal/slot mécanismes, les limites de thread sont croisés correctement et en toute sécurité.

Vous pourriez lancer la synchronisation si vous avez d'appeler des méthodes sur votre objet en dehors de ce mécanisme.

Je sais que Qt a quelques autres belles filetage des installations au-delà de threads qui sont probablement utile de se familiariser avec, mais je n'ai pas encore le faire :)

10voto

Sergey Tachenov Points 8123

Voici un exemple d'utilisation de QThread correctement, mais il a quelques problèmes avec elle, et qui sont reflétées dans les commentaires. En particulier, puisque l'ordre dans lequel les emplacements sont exécutées n'est pas strictement définie, elle pourrait conduire à des problèmes différents. Le commentaire posté le août 6, 2013 donne une belle idée de comment faire face à ce problème. J'utilise quelque chose comme ça dans mon programme, et voici un exemple de code à préciser.

L'idée de base est la même: j'ai créer un QThread instance qui vit dans mon thread principal, un travailleur instance de classe qui vit dans le nouveau fil que j'ai créé, puis-je me connecter à tous les signaux.

void ChildProcesses::start()
{
    QThread *childrenWatcherThread = new QThread();
    ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
    childrenWatcher->moveToThread(childrenWatcherThread);
    // These three signals carry the "outcome" of the worker job.
    connect(childrenWatcher, SIGNAL(exited(int, int)),
            SLOT(onChildExited(int, int)));
    connect(childrenWatcher, SIGNAL(signalled(int, int)),
            SLOT(onChildSignalled(int, int)));
    connect(childrenWatcher, SIGNAL(stateChanged(int)),
            SLOT(onChildStateChanged(int)));
    // Make the watcher watch when the thread starts:
    connect(childrenWatcherThread, SIGNAL(started()),
            childrenWatcher, SLOT(watch()));
    // Make the watcher set its 'stop' flag when we're done.
    // This is performed while the watch() method is still running,
    // so we need to execute it concurrently from this thread,
    // hence the Qt::DirectConnection. The stop() method is thread-safe
    // (uses a mutex to set the flag).
    connect(this, SIGNAL(stopped()),
            childrenWatcher, SLOT(stop()), Qt::DirectConnection);
    // Make the thread quit when the watcher self-destructs:
    connect(childrenWatcher, SIGNAL(destroyed()),
            childrenWatcherThread, SLOT(quit()));
    // Make the thread self-destruct when it finishes,
    // or rather, make the main thread delete it:
    connect(childrenWatcherThread, SIGNAL(finished()),
            childrenWatcherThread, SLOT(deleteLater()));
    childrenWatcherThread->start();
}

Un peu de contexte:

Le ChildProcesses classe est un enfant du gestionnaire de processus qui démarre de nouveaux processus enfants avec spawn() appelle, conserve la liste des processus en cours d'exécution et ainsi de suite. Cependant, il a besoin de garder la trace des enfants unis, ce qui signifie en utilisant waitpid() appel sur Linux ou WaitForMultipleObjects sur Windows. J'ai l'habitude d'appeler ces non-mode de blocage à l'aide d'un timer, mais maintenant je veux plus de réaction rapide, ce qui signifie un mode de blocage. C'est là que le fil entre en jeu.

Le ChildrenWatcher classe est définie comme suit:

class ChildrenWatcher: public QObject {
    Q_OBJECT
private:
    QMutex mutex;
    bool stopped;
    bool isStopped();
public:
    ChildrenWatcher();
public slots:
    /// This is the method which runs in the thread.
    void watch();
    /// Sets the stop flag.
    void stop();
signals:
    /// A child process exited normally.
    void exited(int ospid, int code);
    /// A child process crashed (Unix only).
    void signalled(int ospid, int signal);
    /// Something happened to a child (Unix only).
    void stateChanged(int ospid);
};

Voici comment cela fonctionne. Quand tout ça a commencé, le ChildProcess::méthode start() est appelée (voir ci-dessus). Il crée un nouveau QThread et une nouvelle ChildrenWatcher, qui est ensuite déplacé vers le nouveau thread. Puis-je connecter trois signaux informer mon manager sur le sort de ses processus enfants (sorti/marqué/dieu-sait-ce-qui s'est passé). Commence alors le principal plaisir.

- Je me connecter QThread::commencé() à la ChildrenWatcher::montre() la méthode de sorte qu'il est lancé dès que le fil est prêt. Depuis l'observateur de la vie dans le nouveau thread, c'est là que la montre() la méthode est exécutée (en file d'attente de connexion est utilisé pour appeler la fente).

Puis-je connecter le ChildProcesses::arrêté() signal à la ChildrenWatcher::stop() fente à l'aide de Qt::DirectConnection parce que j'ai besoin de le faire de manière asynchrone. C'est nécessaire pour mon thread s'arrête lorsque la ChildProcesses manager n'est plus nécessaire. La méthode stop() ressemble à ceci:

void ChildrenWatcher::stop()
{
    mutex.lock();
    stopped = true;
    mutex.unlock();
}

Et puis ChildrenWatcher::watch():

void ChildrenWatcher::watch()
{
  while (!isStopped()) {
    // Blocking waitpid() call here.
    // Maybe emit one of the three informational signals here too.
  }
  // Self-destruct now!
  deleteLater();
}

Oh, et la méthode isStopped() est juste un moyen pratique d'utiliser un mutex dans le while() état:

bool ChildrenWatcher::isStopped()
{
    bool stopped;
    mutex.lock();
    stopped = this->stopped;
    mutex.unlock();
    return stopped;
}

Donc ce qui se passe ici est que j'ai mis l'arrêté du pavillon quand j'en ai besoin pour finir, et puis la prochaine fois isStopped() est appelée, elle retourne false, et le thread se termine.

Donc ce qui arrive lorsque la montre() de la boucle se termine? Il appelle deleteLater() donc l'objet d'auto-détruit dès que le contrôle est retourné au fil boucle d'événement qui se produit juste après la deleteLater() appel (lorsque la montre() retourne). Revenir à ChildProcesses::start(), vous pouvez voir qu'il ya une connexion à partir de l'détruit() signal de l'observateur à la quitter() fente du fil. Cela signifie que le thread se termine automatiquement lorsque l'observateur est fait. Et quand c'est fini, il s'auto-détruise trop parce que sa propre fini() signal est connecté à son deleteLater() fente.

C'est à peu près la même idée que Maya posté, mais parce que j'utilise l'auto-destruction de l'idiome, je n'ai pas besoin de dépendre de la séquence dans laquelle les fentes sont appelés. C'est toujours auto-détruire d'abord, arrêter de fil plus tard, puis il s'auto-détruise. Je pourrais définir un produit fini() du signal dans le travailleur, et puis le connecter à son propre deleteLater(), mais cela ne signifie pas une connexion plus. Comme je n'ai pas besoin d'un produit fini() du signal pour tout autre but, j'ai choisi de simplement appeler deleteLater() à partir du travailleur lui-même.

Maya mentionne également que vous ne devriez pas attribuer de nouveaux QObjects les travailleurs constructeur, parce qu'ils ne vivent pas dans le fil que vous déplacez le travailleur à. J'avais dis de le faire de toute façon parce que c'est la façon dont la programmation orientée objet de travaux. Juste assurez-vous que tous ceux QObjects sont les enfants de l'ouvrier (qui est, utiliser le QObject(QObject*) constructeur) - moveToThread() déplace tous les enfants le long avec le déplacement de l'objet. Si vous avez vraiment besoin d'avoir QObjects qui ne sont pas des enfants de votre objet, puis remplacer moveToThread() dans votre travailleur afin qu'elle se déplace tous les objets nécessaires.

3voto

parsley72 Points 804

Sans vouloir nuire à l'excellente réponse de @ sergey-tachenov, vous pouvez cesser d'utiliser SIGNAL et SLOT dans Qt5, simplifier votre code et bénéficier de l'avantage de la vérification du temps de compilation:

 void ChildProcesses::start()
{
    QThread *childrenWatcherThread = new QThread();
    ChildrenWatcher *childrenWatcher = new ChildrenWatcher();
    childrenWatcher->moveToThread(childrenWatcherThread);
    // These three signals carry the "outcome" of the worker job.
    connect(childrenWatcher, ChildrenWatcher::exited,
            ChildProcesses::onChildExited);
    connect(childrenWatcher, ChildrenWatcher::signalled,
            ChildProcesses::onChildSignalled);
    connect(childrenWatcher, ChildrenWatcher::stateChanged,
            ChildProcesses::onChildStateChanged);
    // Make the watcher watch when the thread starts:
    connect(childrenWatcherThread, QThread::started,
            childrenWatcher, ChildrenWatcher::watch);
    // Make the watcher set its 'stop' flag when we're done.
    // This is performed while the watch() method is still running,
    // so we need to execute it concurrently from this thread,
    // hence the Qt::DirectConnection. The stop() method is thread-safe
    // (uses a mutex to set the flag).
    connect(this, ChildProcesses::stopped,
            childrenWatcher, ChildrenWatcher::stop, Qt::DirectConnection);
    // Make the thread quit when the watcher self-destructs:
    connect(childrenWatcher, ChildrenWatcher::destroyed,
            childrenWatcherThread, QThread::quit);
    // Make the thread self-destruct when it finishes,
    // or rather, make the main thread delete it:
    connect(childrenWatcherThread, QThread::finished,
            childrenWatcherThread, QThread::deleteLater);
    childrenWatcherThread->start();
}
 

2voto

Zaid Points 6

sous-classer la classe qthread sera toujours exécuter le code dans le thread d'origine. Je voulais courir un udp auditeur dans une application qui est déjà à l'aide de l'interface graphique Thread(thread principal) et tout mon udp auditeur de travail parfaitement mon GUI a été congelé, car il a été bloqué par le sous-classé qthread des gestionnaires d'événements. Je pense que ce g19fanatic affiché est correct, mais vous aurez également besoin du thread de travail pour réussir à migrer de l'objet pour le nouveau thread. J'ai trouvé ce post qui décrit les détails de la Faire et à Ne pas du filetage dans QT.

Faut Lire avant de vous décider à la sous-classe QThread !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X