Les threads et les QObjectQThread hérite de QObject. Il émet des signaux pour indiquer que le thread a commencé ou a terminé son exécution, et fournit également quelques slots. IntroductionPlus intéressant encore, les QObject peuvent être utilisés dans de multiples threads, émettre des signaux appelant des slots dans d'autres threads, et poster des évènements aux objets qui « vivent » dans d'autres threads. Cela est possible car chaque thread est autorisé à posséder sa propre boucle d'évènements. Sujets :
La ré-entrance de QObjectQObject est ré-entrant(1). La plupart de ses sous-classes non GUI, telles que QTimer, QTcpSocket, QUdpSocket, QFtp et QProcess, sont aussi ré-entrantes, ce qui permet de les utiliser simultanément depuis plusieurs threads. Notez que ces classes sont conçues pour être générées et utilisées au sein d'un seul thread; le fonctionnement n'est pas garanti si l'on crée un objet dans un thread et que l'on appelle ses fonctions depuis un autre thread. Il y a trois contraintes à considérer :
Bien que QObject soit ré-entrant, les classes de GUI comme QWidget et toutes ses sous-classes ne sont pas ré-entrantes. Elles ne peuvent être utilisées qu'à partir du thread principal. Comme précisé précédemment, QCoreApplication::exec() doit aussi être appelée à partir de ce thread. Il est donc impossible d'utiliser les classes de GUI dans d'autres threads que le thread principal. En pratique, cette impossibilité peut être contournée en plaçant les opérations coûteuses en temps de calcul dans un thread de travail séparé. Puis, à la clôture de ce thread, il suffit d'afficher les résultats à l'écran dans le thread principal. C'est l'approche utilisée pour implémenter les exemples Mandelbrot et Blocking Fortune Client. Boucle d'évènements par threadChaque thread peut posséder sa propre boucle évènementielle. Le thread initial démarre sa boucle évènementielle en utilisant QCoreApplication::exec(); les autres threads peuvent démarrer une boucle évènementielle en utilisant QThread::exec(). Comme QCoreApplication, QThread fournit une fonction exit(int) et un slot quit(). Dans un thread, la présence d'une boucle évènementielle est indispensable pour utiliser certaines classes non-GUI (comme QTimer, QTcpSocket, et QProcess). Cela permet également de connecter des signaux à partir de n'importe quel thread aux slots d'un thread spécifique. Cela est expliqué plus en détail dans la section Les signaux et les slots à travers les threads ci-dessous. On dit qu'une instance de QObject « vit » dans le thread dans lequel elle a été créée. Les évènements à destination de cet objet sont envoyés par la boucle évènementielle de ce thread. Le thread dans lequel vit un objet est disponible via QObject::thread(). Notez que QObject::thread() retourne zéro pour les QObjects créés avant QApplication. Cela signifie pour ces objets que le thread principal ne va gérer que les évènements postés; la propagation d'autres évènements n'est pas du tout effectuée pour les objets dépourvus de thread. Utilisez la fonction QObject::moveToThread() pour changer les liens d'un thread avec un objet et ses enfants (un objet ne peut pas être déplacé s'il a un parent). Il n?est pas sûr d?appeler delete sur un QObject depuis un autre thread que le possesseur de l'objet (ou même d'accéder à l'objet par d'autres méthodes), à moins de pouvoir garantir que l'objet n'est pas en train de propager des évènements à cet instant précis. A la place, utilisez plutôt QObject::deleteLater(): un évènement DeferredDelete sera ainsi posté puis traité par la boucle évènementielle du thread quand elle aura la main. Par défaut, le thread possesseur d'un QObject est celui qui l'a créé. Mais ce n'est plus vrai si QObject::moveToThread() a été appelé après la création de l'objet. Si aucune boucle évènementielle n'est lancée, les évènements ne seront pas distribués à l'objet. Par exemple, si vous créez un objet QTimer dans un thread mais que vous n'appelez jamais exec(), le QTimer n'émettra jamais son signal timeout(). L'appel de deleteLater() ne fonctionnera pas non plus. (Ces restrictions s'appliquent également au thread principal.) Il est possible de poster manuellement des évènements dans n'importe quel thread, et à n'importe quel moment, en utilisant la fonction thread-safe QCoreApplication::postEvent(). Les évènements seront automatiquement envoyés par la boucle évènementielle du thread dans lequel l'objet a été créé. Les filtres d'évènements sont utilisables dans tous les threads, à condition que l'objet de contrôle « vive » dans le même thread que l'objet contrôlé. De la même façon, QCoreApplication::sendEvent() est utilisable uniquement pour envoyer des évènements aux objets vivant dans le thread où la fonction est appelée. Cette restriction n'existe pas pour la fonction postEvent()). Accès à des sous-classes de QObject depuis d'autres threadsQObject et toutes ses sous-classes ne sont pas thread-safe. Cela inclut la totalité du système d'envoi des évènements. Il est important de garder à l'esprit que la boucle évènementielle peut envoyer des évènements à votre sous-classe de QObject alors que vous êtes en train d'accéder à l'objet à partir d'un autre thread. Si vous appelez une fonction d'une sous-classe de QObject qui ne vit pas dans le thread actuel et qu'il est possible que l'objet reçoive des évènements, alors vous devez protéger avec un mutex tous les accès aux données internes de votre sous-classe de QObject; dans le cas contraire, des crashs ou des comportements indésirables peuvent survenir. Comme d'autres objets, les QThread vivent dans le thread dans lequel l'objet a été créé -- et non dans le thread qui est créé quand QThread::run() est appelé. Il n'est en général pas sûr de créer des slots dans votre sous-classe de QThread, à moins de protéger les variables membres avec un mutex. D'un autre côté, vous pouvez émettre de façon sûre des signaux à partir d'une implémentation de QThread::run() car l'émission de signaux est thread-safe(2). Les signaux et les slots à travers les threadsQt supporte les types de connexions « signal-slot » suivants :
Le type de connexion peut être spécifié en passant un argument supplémentaire à connect(). Soyez attentifs au fait qu'utiliser des connexions directes quand l'émetteur et le récepteur vivent dans des threads différents n'est pas sûr si une boucle évènementielle est lancée dans le thread du récepteur. Cela n'est pas sûr pour la même raison qu'il n'est pas sûr d'appeler une fonction d'un objet vivant dans un autre thread. QObject::connect() lui-même est thread-safe. L'exemple Mandelbrot utilise une Queued Connection pour communiquer entre un thread de travail et le thread principal. Pour éviter de bloquer la boucle évènementielle du thread principal (et, par conséquent, l'interface utilisateur de l'application), tous les calculs des fractales de Mandelbrot sont effectués dans un thread de travail séparé. 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 séparé pour communiquer de façon asynchrone avec un serveur TCP. RemerciementsMerci à Florent Renault pour la traduction et à Jonathan Courtois ainsi qu'à Philippe Beaucart pour leur relecture !
|
Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. | Qt 4.6 | |
Copyright © 2024 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD. | ||
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP ! |