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.