Sur cette page

Synchronisation des threads

Bien que les threads aient pour but de permettre l'exécution parallèle du code, il arrive parfois qu'un thread doive s'arrêter et attendre un autre thread. Par exemple, si deux threads tentent d'écrire simultanément dans la même variable, le résultat est indéfini. Le principe consistant à forcer les threads à s'attendre les uns les autres s'appelle l'exclusion mutuelle. Il s'agit d'une technique courante pour protéger les ressources partagées telles que les données.

Qt fournit à la fois des primitives de bas niveau et des mécanismes de haut niveau pour la synchronisation des threads.

Primitives de synchronisation de bas niveau

QMutex est la classe de base permettant d’imposer l’exclusion mutuelle. Un thread verrouille un mutex afin d’accéder à une ressource partagée. Si un deuxième thread tente de verrouiller le mutex alors qu’il est déjà verrouillé, ce deuxième thread sera mis en veille jusqu’à ce que le premier thread ait terminé sa tâche et déverrouillé le mutex.

QReadWriteLock est similaire à QMutex, à la différence qu’il fait la distinction entre les accès « en lecture » et « en écriture ». Lorsqu’aucune écriture n’est en cours sur une donnée, plusieurs threads peuvent la lire simultanément en toute sécurité. Un verrou de lecture ( QMutex ) oblige les lecteurs multiples à se relayer pour lire les données partagées, tandis qu’un verrou de lecture-écriture ( QReadWriteLock ) autorise la lecture simultanée, améliorant ainsi le parallélisme.

QSemaphore est une généralisation de QMutex qui protège un certain nombre de ressources identiques. À l’inverse, un QMutex protège exactement une seule ressource. L’exemple « Producteur et consommateur utilisant des sémaphores » illustre une application typique des sémaphores : la synchronisation de l’accès à un tampon circulaire entre un producteur et un consommateur.

QWaitCondition synchronise les threads non pas en imposant une exclusion mutuelle, mais en fournissant une variable de condition. Alors que les autres primitives font attendre les threads jusqu’à ce qu’une ressource soit déverrouillée, QWaitCondition fait attendre les threads jusqu’à ce qu’une condition particulière soit remplie. Pour permettre aux threads en attente de poursuivre leur exécution, appelez wakeOne() pour réveiller un thread sélectionné aléatoirement ou wakeAll() pour les réveiller tous simultanément. L’exemple « Producteur et consommateur utilisant des conditions d’attente » montre comment résoudre le problème du producteur-consommateur en utilisant QWaitCondition au lieu de QSemaphore.

Remarque : les classes de synchronisationde Qt reposent sur l’utilisation de pointeurs correctement alignés. Par exemple, vous ne pouvez pas utiliser de classes « packed » avec MSVC.

Ces classes de synchronisation peuvent être utilisées pour rendre une méthode « thread-safe ». Cependant, cela entraîne une perte de performances, ce qui explique pourquoi la plupart des méthodes Qt ne sont pas rendues « thread-safe ».

Risques

Si un thread verrouille une ressource mais ne la déverrouille pas, l’application peut se bloquer car la ressource deviendra définitivement indisponible pour les autres threads. Cela peut se produire, par exemple, si une exception est levée et force la fonction en cours à retourner sans libérer son verrou.

Un autre scénario similaire est celui d’un blocage (deadlock). Supposons, par exemple, que le thread A attende que le thread B déverrouille une ressource. Si le thread B attend également que le thread A déverrouille une autre ressource, les deux threads finiront par attendre indéfiniment, ce qui entraînera le blocage de l’application.

Classes de commodité

QMutexLocker, QReadLocker et QWriteLocker sont des classes de commodité qui facilitent l’utilisation de QMutex et QReadWriteLock. Elles verrouillent une ressource lors de leur construction et la déverrouillent automatiquement lors de leur destruction. Elles sont conçues pour simplifier le code utilisant QMutex et QReadWriteLock, réduisant ainsi le risque qu’une ressource soit verrouillée de manière permanente par accident.

Files d’attente d’événements de haut niveau

Le système d’événements de Qt est très utile pour la communication entre threads. Chaque thread peut disposer de sa propre boucle d’événements. Pour appeler un slot (ou toute méthode de type « invokable ») dans un autre thread, placez cet appel dans la boucle d’événements du thread cible. Cela permet au thread cible de terminer sa tâche en cours avant que le slot ne commence à s’exécuter, tandis que le thread d’origine continue de s’exécuter en parallèle.

Pour placer un appel dans une boucle d’événements, établissez une connexion signal-slot mise en file d’attente. Chaque fois que le signal est émis, ses arguments sont enregistrés par le système d’événements. Le thread auquel correspond l’ lives in du récepteur du signal exécutera alors le slot. Vous pouvez également appeler ` QMetaObject::invokeMethod()` pour obtenir le même effet sans recourir aux signaux. Dans les deux cas, il est impératif d’utiliser un ` queued connection `, car un ` direct connection ` contourne le système d’événements et exécute immédiatement la méthode dans le thread actuel.

Il n’y a aucun risque de blocage (deadlock) lorsque l’on utilise le système d’événements pour la synchronisation des threads, contrairement à l’utilisation de primitives de bas niveau. Cependant, le système d’événements n’impose pas l’exclusion mutuelle. Si des méthodes invocables accèdent à des données partagées, elles doivent tout de même être protégées à l’aide de primitives de bas niveau.

Cela dit, le système d'événements de Qt, associé à des structures de données implicitement partagées, offre une alternative au verrouillage traditionnel des threads. Si l'on utilise exclusivement des signaux et des slots et qu'aucune variable n'est partagée entre les threads, un programme multithread peut se passer complètement des primitives de bas niveau.

Voir également QThread::exec() et Threads et QObjects.

© 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.