Quand faut-il utiliser un spinlock au lieu du mutex?

Je pense que les deux font le même travail, comment décidez-vous lequel utiliser pour la synchronisation?

    La théorie

    En théorie, lorsqu’un thread essaie de verrouiller un mutex et que celui-ci échoue, car le mutex est déjà verrouillé, il s’endort, permettant immédiatement à un autre thread de s’exécuter. Il continuera à dormir jusqu’à son réveil, ce qui sera le cas une fois que le mutex sera déverrouillé par le thread qui le retenait auparavant. Lorsqu’un thread essaie de verrouiller un spinlock et qu’il ne réussit pas, il réessayera continuellement de le verrouiller jusqu’à ce qu’il réussisse; ainsi, il ne permettra pas à un autre thread de prendre sa place (cependant, le système d’exploitation basculera forcément sur un autre thread, bien sûr, une fois que le quantum d’exécution du processeur du thread actuel aura été dépassé).

    Le problème

    Le problème avec les mutex est que mettre les threads en veille et les réveiller à nouveau sont des opérations assez coûteuses, elles nécessiteront pas mal d’instructions de processeurs et prendront aussi du temps. Si maintenant le mutex n’était verrouillé que pendant un très court laps de temps, le temps passé à mettre un thread en veille et à le réveiller pourrait dépasser le temps que le thread a réellement dormi et même dépasser le temps ont gaspillé en sondant constamment sur un spinlock. D’un autre côté, l’interrogation sur un verrou tournant gaspille constamment du temps processeur et si le verrou est maintenu plus longtemps, cela gaspille beaucoup plus de temps processeur et il aurait été préférable que le thread dorme plutôt.

    La solution

    Utiliser des spinlocks sur un système single-core / single-CPU n’a généralement aucun sens, car tant que l’interrogation du verrou tournant bloque le seul cœur de processeur disponible, aucun autre thread ne peut s’exécuter et aucun autre thread ne pouvant s’exécuter, le verrou ne fonctionnera pas. être débloqué non plus. IOW, un spinlock ne gaspille que du temps CPU sur ces systèmes sans réel bénéfice. Si le thread a été mis en veille à la place, un autre thread aurait pu être exécuté en même temps, débloquant éventuellement le verrou, puis autorisant le premier thread à continuer le traitement, une fois qu’il se réveillerait à nouveau.

    Sur un système multi-core / multi-CPU, avec beaucoup de verrous conservés pendant très peu de temps, le temps perdu à mettre constamment les threads en veille et à les réveiller à nouveau peut diminuer sensiblement les performances d’exécution. A la place des spinlocks, les threads ont la possibilité de tirer parti de leur quantum d’exécution complet (ne bloquant toujours que pour une période de temps très courte, puis poursuivant immédiatement leur travail), ce qui entraîne un débit de traitement beaucoup plus élevé.

    La pratique

    Etant donné que très souvent les programmeurs ne peuvent pas savoir à l’avance si les mutex ou les spinlocks seront meilleurs (par exemple parce que le nombre de cœurs de CPU de l’architecture cible est inconnu), les systèmes d’exploitation ne peuvent pas savoir environnements multi-coeurs, la plupart des systèmes ne font pas de distinction ssortingcte entre les mutex et les spinlocks. En fait, la plupart des systèmes d’exploitation modernes ont des mutex hybrides et des spinlocks hybrides. Qu’est-ce que cela signifie réellement?

    Un mutex hybride se comporte comme un spinlock sur un système multi-core. Si un thread ne peut pas verrouiller le mutex, il ne sera pas immédiatement mis en veille, car le mutex pourrait être déverrouillé très rapidement. Au lieu de cela, le mutex se comportera exactement comme un spinlock. Ce n’est que si le verrou n’a toujours pas été obtenu après un certain temps (ou des tentatives ou tout autre facteur de mesure) que le thread est réellement endormi. Si le même code s’exécute sur un système avec un seul cœur, le mutex ne sera pas bloqué, comme indiqué ci-dessus.

    Un spinlock hybride se comporte comme un spinlock normal au début, mais pour éviter de gaspiller trop de temps CPU, il peut avoir une stratégie de sauvegarde. Il ne mettra généralement pas le thread en veille (car vous ne voulez pas que cela se produise lors de l’utilisation d’un spinlock), mais il peut décider d’arrêter le thread (immédiatement ou après un certain temps) et permettre à un autre thread de s’exécuter , augmentant ainsi les chances de délocking du verrou tournant (un commutateur de thread pur est généralement moins coûteux que celui qui implique de mettre un thread en veille et de le réactiver ultérieurement, mais pas de loin).

    Résumé

    En cas de doute, utilisez des mutex, ils sont généralement le meilleur choix et la plupart des systèmes modernes leur permettent de tourner pendant très peu de temps, si cela semble bénéfique. Utiliser des spinlocks peut parfois améliorer les performances, mais seulement dans certaines conditions et le fait que vous ayez des doutes me dit plutôt que vous ne travaillez sur aucun projet pour lequel un spinlock pourrait être bénéfique. Vous pourriez envisager d’utiliser votre propre “object de locking”, qui peut utiliser un spinlock ou un mutex en interne (par exemple, ce comportement pourrait être configurable lors de la création d’un tel object), utiliser des mutex partout et si vous pensez que help, essayez-le et comparez les résultats (par exemple en utilisant un profileur), mais assurez-vous de tester les deux cas, un système monocœur et un système multi-core avant de tirer des conclusions (et éventuellement différents systèmes d’exploitation, si votre code sera multi-plateforme).

    En continuant avec la suggestion de Mecki, cet article pthread sur le blog d’Alexander Sandler, Alex sur Linux montre comment le spinlock & mutexes peuvent être implémentés pour tester le comportement en utilisant #ifdef.

    Cependant, assurez-vous de prendre l’appel final en fonction de votre observation, étant donné que l’exemple donné est un cas isolé, votre exigence de projet, l’environnement peut être entièrement différent.

    Notez également que sur certains environnements et conditions (par exemple, lorsque vous utilisez des fenêtres au niveau de la dissortingbution> = DISPATCH LEVEL), vous ne pouvez pas utiliser le mutex mais plutôt le spinlock. Sur unix – même chose.

    Voici une question équivalente sur le site unix du concurrent stackexchange: https://unix.stackexchange.com/questions/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something- plus

    Informations sur l’envoi sur les systèmes Windows: http://download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc

    La réponse de Mecki le cloue assez bien. Cependant, sur un seul processeur, l’utilisation d’un verrou tournant peut avoir un sens lorsque la tâche est en attente sur le verrou à donner par une routine de service d’interruption. L’interruption transfèrerait le contrôle à l’ISR, ce qui préparerait la ressource à être utilisée par la tâche en attente. Cela se terminerait en libérant le verrou avant de reprendre le contrôle de la tâche interrompue. La tâche de rotation trouverait le verrou tournant disponible et continuerait.

    Les mécanismes de synchronisation Spinlock et Mutex sont très courants aujourd’hui pour être vus.

    Pensons d’abord à Spinlock.

    Fondamentalement, il s’agit d’une action en attente, ce qui signifie que nous devons attendre qu’un verrou spécifié soit libéré avant de pouvoir passer à l’action suivante. Conceptuellement très simple, en l’implémentant, ce n’est pas le cas. Par exemple: si le verrou n’a pas été libéré, le thread se remplaçait et passait en mode veille. Devrions-nous le gérer? Comment gérer les verrous de synchronisation lorsque deux threads demandent simultanément un access?

    Généralement, l’idée la plus intuitive concerne la synchronisation via une variable pour protéger la section critique. Le concept de Mutex est similaire, mais ils sont toujours différents. Focus sur: l’utilisation du processeur. Spinlock consum du temps CPU pour attendre l’action et, par conséquent, nous pouvons résumer la différence entre les deux:

    Dans les environnements multi-core homogènes, si le temps passé sur une section critique est faible, utilisez Spinlock, car nous pouvons réduire le temps de changement de contexte. (La comparaison avec un seul cœur n’est pas importante, car certains systèmes implémentent Spinlock au milieu du commutateur)

    Sous Windows, l’utilisation de Spinlock mettra à jour le thread vers DISPATCH_LEVEL, ce qui dans certains cas peut ne pas être autorisé, donc cette fois, nous avons dû utiliser un Mutex (APC_LEVEL).

    Utiliser des spinlocks sur un système single-core / single-CPU n’a généralement aucun sens, car tant que l’interrogation du verrou tournant bloque le seul cœur de processeur disponible, aucun autre thread ne peut s’exécuter et aucun autre thread ne pouvant s’exécuter, le verrou ne fonctionnera pas. être débloqué non plus. IOW, un verrou tournant ne gaspille que du temps CPU sur ces systèmes sans réel bénéfice

    C’est faux. Il n’y a pas de gaspillage de cycles de processeur en utilisant des spinlocks sur des systèmes de processeurs uni, car une fois qu’un processus prend un spin-lock, la préemption est désactivée, de sorte que personne ne peut tourner! C’est juste que l’utiliser n’a aucun sens! Par conséquent, les spinlocks sur les systèmes Uni sont remplacés par preempt_disable au moment de la compilation par le kernel!