Sur cette page

Synchronisation des threads

Bien que l'objectif des threads soit de permettre au code de s'exécuter en parallèle, il arrive que les threads doivent s'arrêter et attendre d'autres threads. Par exemple, si deux threads essaient d'écrire simultanément dans la même variable, le résultat n'est pas dé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 des primitives de bas niveau ainsi que des mécanismes de haut niveau pour synchroniser les threads.

Primitives de synchronisation de bas niveau

QMutex est la classe de base pour appliquer 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é, le deuxième thread sera mis en veille jusqu'à ce que le premier thread termine sa tâche et déverrouille le mutex.

QReadWriteLock est similaire à QMutex, sauf qu'il fait la distinction entre l'accès en "lecture" et l'accès en "écriture". Lorsqu'une donnée n'est pas en cours d'écriture, plusieurs threads peuvent la lire simultanément. Une adresse QMutex oblige plusieurs lecteurs à se relayer pour lire les données partagées, mais une adresse QReadWriteLock permet une lecture simultanée, ce qui améliore le parallélisme.

QSemaphore est une généralisation de QMutex qui protège un certain nombre de ressources identiques. En revanche, un QMutex protège exactement une ressource. L'exemple du producteur et du consommateur utilisant des s émaphores montre une application typique des sémaphores : la synchronisation de l'accès à un tampon circulaire entre un producteur et un consommateur.

QWaitCondition Le sémaphore synchronise les threads non pas en appliquant l'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 continuer, appelez wakeOne() pour réveiller un thread choisi au hasard ou wakeAll() pour les réveiller tous simultanément. L'exemple Producer and Consumer using Wait Conditions montre comment résoudre le problème producteur-consommateur en utilisant QWaitCondition au lieu de QSemaphore.

Note : Les classes de synchronisation de Qt reposent sur l'utilisation de pointeurs correctement alignés. Par exemple, il n'est pas possible d'utiliser des classes emballées avec MSVC.

Ces classes de synchronisation peuvent être utilisées pour rendre une méthode "thread safe". Cependant, cela entraîne une pénalité en termes de performances, c'est pourquoi la plupart des méthodes Qt ne sont pas rendues sûres pour les threads.

Risques

Si un thread verrouille une ressource mais ne la déverrouille pas, l'application peut se bloquer car la ressource devient 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 à revenir sans libérer son verrou.

Un autre scénario similaire est celui d'un blocage. 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 et l'application se bloquera.

Classes de commodité

QMutexLockerLes classes de commodité QReadLocker et QWriteLocker facilitent l'utilisation de QMutex et QReadWriteLock. Elles verrouillent une ressource lorsqu'elles sont construites et la déverrouillent automatiquement lorsqu'elles sont détruites. Elles sont conçues pour simplifier le code qui utilise QMutex et QReadWriteLock, réduisant ainsi les risques 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 les threads. Chaque thread peut avoir sa propre boucle d'événements. Pour appeler un slot (ou toute méthode 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 actuelle avant que le slot ne commence à fonctionner, tandis que le thread d'origine continue à fonctionner en parallèle.

Pour placer une invocation dans une boucle d'événements, établissez une connexion signal-emplacement en file d'attente. Chaque fois que le signal est émis, ses arguments sont enregistrés par le système d'événements. Le fil d'exécution du récepteur de signal lives in exécutera alors le slot. Vous pouvez également appeler QMetaObject::invokeMethod() pour obtenir le même effet sans signaux. Dans les deux cas, un queued connection doit être utilisé car un direct connection contourne le système d'événements et exécute la méthode immédiatement dans le thread courant.

L'utilisation du système d'événements pour la synchronisation des threads ne présente aucun risque de blocage, contrairement à l'utilisation de primitives de bas niveau. Cependant, le système d'événements n'applique pas l'exclusion mutuelle. Si des méthodes invocables accèdent à des données partagées, elles doivent toujours être protégées par des primitives de bas niveau.

Cela dit, le système d'événements de Qt, ainsi que les structures de données implicitement partagées, offrent une alternative au verrouillage traditionnel des threads. Si les signaux et les slots sont utilisés exclusivement et qu'aucune variable n'est partagée entre les threads, un programme multithread peut se passer complètement de 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.