Sur cette page

Les threads et les QObjects

QThread hérite de ` QObject`. Il émet des signaux pour indiquer que le thread a commencé ou terminé son exécution, et fournit également quelques slots.

Plus intéressant encore, les objets de type ` QObject` peuvent être utilisés dans plusieurs threads, émettre des signaux qui invoquent des slots dans d’autres threads et envoyer des événements à des objets qui « vivent » dans d’autres threads. Cela est possible car chaque thread est autorisé à disposer de sa propre boucle d’événements.

Réentrance de QObject

QObject est réentrant. La plupart de ses sous-classes non liées à l’interface graphique, telles que QTimer, QTcpSocket, QUdpSocket et QProcess, sont également réentrantes, ce qui permet d’utiliser ces classes simultanément depuis plusieurs threads. Notez que ces classes sont conçues pour être créées et utilisées au sein d’un seul thread ; la création d’un objet dans un thread et l’appel de ses fonctions depuis un autre thread ne sont pas garantis de fonctionner. Il existe trois contraintes à prendre en compte :

  • Le fils d’un objet ` QObject ` doit toujours être créé dans le thread où le parent a été créé. Cela implique, entre autres, que vous ne devez jamais passer l’objet ` QThread ` (`this`) comme parent d’un objet créé dans ce thread (puisque l’objet ` QThread ` lui-même a été créé dans un autre thread).
  • Les objets pilotés par les événements ne peuvent être utilisés que dans un seul thread. Cela s’applique notamment au mécanisme de minuterie et au network module. Par exemple, vous ne pouvez pas démarrer une minuterie ou connecter un socket dans un thread autre que le object's thread.
  • Vous devez vous assurer que tous les objets créés dans un thread sont supprimés avant de supprimer l’ QThread. Cela peut être facilement réalisé en créant les objets sur la pile dans votre implémentation de run().

Bien que ` QObject ` soit réentrant, les classes d’interface graphique, notamment ` QWidget ` et toutes ses sous-classes, ne sont pas réentrantes. Elles ne peuvent être utilisées qu’à partir du thread principal. Comme indiqué précédemment, la fonction ` QCoreApplication::exec()` doit également être appelée à partir de ce thread.

En pratique, l’impossibilité d’utiliser les classes d’interface graphique dans d’autres threads que le thread principal peut facilement être contournée en plaçant les opérations chronophages dans un thread de travail distinct et en affichant les résultats à l’écran dans le thread principal une fois que le thread de travail a terminé. C’est l’approche utilisée pour implémenter l’exemple Mandelbrot et l’exemple Blocking Fortune Client.

En général, la création de QObjects avant l’ QApplication n’est pas prise en charge et peut entraîner des plantages inattendus à la fermeture, selon la plateforme. Cela signifie que les instances statiques de QObject ne sont pas non plus prises en charge. Une application mono- ou multithread correctement structurée doit faire en sorte que l’ QApplication soit le premier élément créé et le dernier détruit QObject.

Boucle d’événements par thread

Chaque thread peut disposer de sa propre boucle d’événements. Le thread initial démarre sa boucle d’événements à l’aide de ` QCoreApplication::exec()` ou, pour les applications GUI à dialogue unique, parfois à l’aide de ` QDialog::exec()`. Les autres threads peuvent démarrer une boucle d’événements à l’aide de ` QThread::exec()`. À l’instar de ` QCoreApplication`, ` QThread ` fournit une fonction ` exit(int)` et un slot ` quit()`.

Une boucle d’événements dans un thread permet à ce dernier d’utiliser certaines classes Qt non liées à l’interface graphique qui nécessitent la présence d’une boucle d’événements (telles que QTimer, QTcpSocket et QProcess). Elle permet également de connecter des signaux provenant de n’importe quel thread aux slots d’un thread spécifique. Ceci est expliqué plus en détail dans la section « Signaux et slots entre threads » ci-dessous.

Fils d'exécution, objets et boucles d'événements

Une instance de ` QObject ` est dite résider dans le thread dans lequel elle est créée. Les événements destinés à cet objet sont distribués par la boucle d’événements de ce thread. Le thread dans lequel réside une instance de ` QObject ` peut être obtenu à l’aide de la fonction ` QObject::thread()`.

La fonction ` QObject::moveToThread()` modifie l’affinité de thread d’un objet et de ses enfants (l’objet ne peut pas être déplacé s’il possède un parent).

Appeler delete sur un QObject depuis un thread autre que celui qui détient l’objet (ou accéder à l’objet par d’autres moyens) est dangereux, à moins que vous ne garantissiez que l’objet ne traite pas d’événements à ce moment-là. Utilisez plutôt ` QObject::deleteLater()` ; un événement ` DeferredDelete ` sera alors publié, et la boucle d’événements du thread de l’objet finira par le récupérer. Par défaut, le thread propriétaire d’ un ` QObject ` est celui qui a créé l’` QObject`, mais ce n’est plus le cas une fois que ` QObject::moveToThread()` a été appelée.

Si aucune boucle d’événements n’est en cours d’exécution, les événements ne seront pas transmis à l’objet. Par exemple, si vous créez un objet ` QTimer ` dans un thread mais que vous n’appelez jamais ` exec()`, l’objet ` QTimer ` n’émettra jamais son signal ` timeout()`. L’appel à ` deleteLater()` ne fonctionnera pas non plus. (Ces restrictions s’appliquent également au thread principal.)

Vous pouvez à tout moment envoyer manuellement des événements à n’importe quel objet dans n’importe quel thread à l’aide de la fonction thread-safe QCoreApplication::postEvent(). Les événements seront automatiquement distribués par la boucle d’événements du thread dans lequel l’objet a été créé.

Les filtres d’événements sont pris en charge dans tous les threads, à condition que l’objet de surveillance se trouve dans le même thread que l’objet surveillé. De même, la fonction ` QCoreApplication::sendEvent()` (contrairement à ` postEvent()`) ne peut être utilisée que pour diffuser des événements vers des objets se trouvant dans le thread à partir duquel la fonction est appelée.

Accès aux sous-classes de QObject depuis d’autres threads

QObject et toutes ses sous-classes ne sont pas sûres inter-threads. Cela inclut l’ensemble du système de diffusion d’événements. Il est important de garder à l’esprit que la boucle d’événements peut diffuser des événements vers votre sous-classe de ` QObject ` pendant que vous accédez à l’objet depuis un autre thread.

Si vous appelez une fonction sur une sous-classe d’ QObject qui ne réside pas dans le thread actuel et que l’objet est susceptible de recevoir des événements, vous devez protéger tous les accès aux données internes de votre sous-classe d’ QObject à l’aide d’un mutex ; sinon, vous risquez de rencontrer des plantages ou d’autres comportements indésirables.

Comme les autres objets, les objets ` QThread ` résident dans le thread où ils ont été créés — etnon dans le thread créé lors de l’appel à ` QThread::run()`. Il est généralement risqué de fournir des slots dans votre sous-classe ` QThread `, à moins que vous ne protégiez les variables membres à l’aide d’un mutex.

En revanche, vous pouvez émettre des signaux en toute sécurité depuis votre implémentation de ` QThread::run()`, car l’émission de signaux est sûre d’un point de vue des threads.

Signaux et slots entre threads

Qt prend en charge les types de connexion signal-slot suivants :

  • Auto Connection (par défaut) Si le signal est émis dans le thread auquel l’objet récepteur a une affinité, le comportement est identique à celui de la connexion directe. Sinon, le comportement est identique à celui de la connexion en file d’attente.
  • Direct Connection Le slot est appelé immédiatement, dès que le signal est émis. Le slot est exécuté dans le thread de l’émetteur, qui n’est pas nécessairement celui du récepteur.
  • Queued Connection Le slot est appelé lorsque le contrôle revient à la boucle d’événements du thread du récepteur. Le slot est exécuté dans le thread du récepteur.
  • Blocking Queued Connection Le slot est appelé comme pour la connexion en file d’attente, sauf que le thread actuel se bloque jusqu’à ce que le slot revienne.

    Remarque : l’utilisation de ce type pour connecter des objets dans le même thread provoquera un blocage (deadlock).

  • Unique Connection Le comportement est identique à celui de la connexion automatique, mais la connexion n’est établie que si elle ne fait pas double emploi avec une connexion existante. Autrement dit, si le même signal est déjà connecté au même slot pour la même paire d’objets, alors la connexion n’est pas établie et la fonction connect() renvoie la valeur « false ».

Le type de connexion peut être spécifié en passant un argument supplémentaire à connect(). Sachez que l’utilisation de connexions directes lorsque l’émetteur et le récepteur se trouvent dans des threads différents n’est pas sûre si une boucle d’événements s’exécute dans le thread du récepteur, pour la même raison que l’appel de n’importe quelle fonction sur un objet se trouvant dans un autre thread n’est pas sûr.

QObject::connect() est quant à elle sûre au niveau des threads.

L’exemple Mandelbrot utilise une connexion en file d’attente pour la communication entre un thread de travail et le thread principal. Afin d’éviter le gel de la boucle d’événements du thread principal (et, par conséquent, de l’interface utilisateur de l’application), tout le calcul de la fractale de Mandelbrot est effectué dans un thread de travail distinct. Le thread émet un signal lorsqu’il a terminé le rendu de la fractale.

De même, l’exemple « Blocking Fortune Client » utilise un thread distinct pour communiquer de manière asynchrone avec un serveur TCP.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.