Qu'est-ce que Verrou tournant (spinlock) ?
Verrou dont l'attente consiste à tester en boucle la variable de verrou jusqu'à ce qu'il se libère. Rapide pour les attentes courtes, mais le CPU reste occupé pendant l'attente.
Nom japonais: スピンロック
« Verrou tournant (spinlock) » apparaît dans 4 questions d'entraînement réparties sur 1 thèmes, dont Multicœur et synchronisation.
Domaine: Multicœur et synchronisation
Comment un verrou tournant s'acquiert et se libère
Un verrou tournant est une variable partagée, valant généralement 0 lorsqu'il est libre et 1 lorsqu'il est pris. Pour l'acquérir, un thread utilise une instruction atomique de lecture-modification-écriture comme test-and-set, swap ou compare-and-swap, qui met la variable à 1 et renvoie l'ancienne valeur en une seule étape indivisible ; en RISC-V, il s'agit d'un swap atomique ou d'une paire load-reserved/store-conditional. Si l'ancienne valeur était 0, le thread possède le verrou ; sinon, il réessaie en boucle. La libération consiste simplement à écrire 0. L'acquisition et la libération doivent aussi agir comme des barrières mémoire, afin que les accès de la section critique restent entre les deux.
Verrou tournant ou mutex
Un mutex qui endort place le thread en attente dans un état bloqué et cède le CPU à d'autres threads ; le système d'exploitation le réveille à la libération du verrou. Cela ne gaspille aucun temps CPU, mais coûte un appel système, un ordonnancement et un changement de contexte, généralement plusieurs microsecondes. Un verrou tournant évite entièrement ce surcoût et l'emporte lorsque le verrou n'est détenu que brièvement, moins longtemps que ne prendrait un cycle d'endormissement et de réveil. Pour les durées de détention longues, l'équilibre s'inverse ; de nombreux mutex de bibliothèque tournent donc d'abord brièvement puis s'endorment, combinant les avantages des deux.
Où les verrous tournants conviennent et où ils échouent
Les verrous tournants sont courants dans les noyaux de systèmes d'exploitation, pour les sections critiques courtes sur multiprocesseurs et pour les données partagées avec les gestionnaires d'interruption, qui ne peuvent pas dormir. Le code qui détient un verrou tournant ne doit pas dormir, et les noyaux désactivent généralement la préemption, ainsi que les interruptions si nécessaire, pendant sa détention, afin qu'un gestionnaire d'interruption sur le même cœur ne tourne pas indéfiniment sur un verrou détenu par son propre cœur. Sur un système monocœur, un verrou tournant en espace utilisateur est particulièrement coûteux : le détenteur ne peut pas s'exécuter pour libérer le verrou pendant que l'autre thread tourne, si bien que ce dernier consomme toute sa tranche de temps.
Questions fréquentes
Quelle est la différence entre un verrou tournant et un mutex ?
Un verrou tournant attend en bouclant sur la variable de verrou et garde le CPU occupé, ce qui n'est efficace que pour des attentes très courtes. Un mutex endort le thread en attente et laisse d'autres threads s'exécuter, ce qui convient aux attentes plus longues mais ajoute le coût de l'endormissement, du réveil et du changement de contexte.
Quand faut-il utiliser un verrou tournant ?
Utilisez-en un lorsque la section critique est très courte et que les threads s'exécutent sur des cœurs différents, ou dans des contextes noyau comme les gestionnaires d'interruption, qui n'ont pas le droit de dormir. Évitez-le lorsque le verrou peut être détenu longtemps ou sur les systèmes monocœurs.
Qu'est-ce que le test-and-test-and-set ?
C'est une technique de verrou tournant dans laquelle les threads en attente lisent en boucle le verrou par des chargements ordinaires depuis leur propre cache et ne tentent le test-and-set atomique que lorsque le verrou semble libre. Cela évite les invalidations continuelles de lignes de cache et réduit fortement le trafic de cohérence.