175 votes

Verrouillage récursif (Mutex) vs Verrouillage non récursif (Mutex)

POSIX permet mutex être récursive. Cela signifie que le même thread peut verrouiller le même mutex deux fois et ne fait pas l'impasse. Bien sûr, elle a aussi besoin de déverrouiller deux fois, sinon, aucun autre thread ne peut obtenir le mutex. Pas tous les systèmes à l'appui de pthreads également en charge récursive mutex, mais si ils veulent être conforme POSIX, ils ont pour.

D'autres Api (plus haut niveau de l'Api) aussi offrent généralement des mutex, souvent appelé les Verrous. Certains systèmes/langues (par exemple le Cacao Objective-C) offrir à la fois récursives et non récursives mutex. Certains langages offrent seulement un ou de l'autre. E. g. en Java, les mutex sont toujours récursive (le même thread peut deux fois "synchroniser" sur le même objet). Selon ce que les autres fil des fonctionnalités qu'ils offrent, de ne pas avoir récursive mutex peut-être pas de problème, car ils peuvent facilement être écrit vous-même (je l'ai déjà mis en œuvre récursive mutex moi-même sur la base de plus simple mutex/état des opérations).

Ce que je ne comprends pas vraiment: Ce sont les non-récursive mutex? Pourquoi voudrais-je avoir un blocage de thread si elle verrouille la même mutex deux fois? Même à haut niveau de langues qui pourrait éviter que (par exemple test si cette situation de blocage et de lancer une exception si c'est le cas) n'est généralement pas le faire. Ils vont laisser le fil de blocage à la place.

Est-ce seulement pour les cas où j'ai accidentellement de verrouillage à deux reprises et ne déverrouiller une seule fois, et dans le cas d'un appel récursif à mutex, il serait plus difficile de trouver le problème, donc je l'ai impasse immédiatement pour voir où le verrouillage incorrect s'affiche? Mais ne pourrais-je pas faire de même avec un verrouillage compteur retourné lors du déverrouillage de et dans une situation où je suis sûr que j'ai publié le dernier lock et le compteur n'est pas à zéro, je peux jeter une exception ou journal le problème? Ou est-il plus utile de cas d'utilisation non récursive mutex que je n'arrive pas à voir? Ou est-ce peut-être juste de la performance, en tant que non-récursive mutex peut être légèrement plus rapide qu'un récursive? Cependant, j'ai testé et la différence est vraiment pas si grand que ça.

146voto

Tall Jeff Points 6065

La différence entre un récursives et non récursives mutex a à voir avec la propriété. Dans le cas d'un appel récursif à mutex, le noyau a à suivre le fil qui a effectivement obtenu le mutex la première fois autour de sorte qu'il peut détecter la différence entre la récursivité par rapport à un autre thread qui doit bloquer la place. Comme une autre réponse a souligné, il y a une question de la surcharge supplémentaire de ce à la fois en termes de mémoire pour stocker ce contexte et aussi les cycles requis pour le maintien.

Cependant, il y a d'autres considérations qui entrent en jeu ici, aussi.

Parce que le récursive mutex a un sens de la propriété, le fil qui saisit le mutex doit être le même thread que la libération du mutex. Dans le cas de non-récursive de mutex, il n'y a pas de sentiment de propriété et de n'importe quel thread peut généralement de libérer le mutex n'importe quel thread d'abord pris le mutex. Dans de nombreux cas, ce type de "mutex" est vraiment plus d'un sémaphore d'action, où vous n'êtes pas nécessairement en utilisant le mutex comme une exclusion de l'appareil, mais l'utiliser pour la synchronisation ou appareil de signalisation entre deux ou plusieurs threads.

Une autre propriété est livré avec un sens de la propriété dans un mutex est la capacité de support d'héritage de priorité. Parce que le noyau peut suivre le fil de posséder le mutex et l'identité de tous les bloqueur(s), dans un la priorité filetée système, il devient possible de s'aggraver, la priorité du thread qui possède actuellement le mutex à la priorité du thread de priorité plus élevé qui bloque actuellement sur le mutex. Cet héritage évite le problème d'inversion de priorité qui peuvent se produire dans de tels cas. (Notez que tous les systèmes d'appui d'héritage de priorité sur ces mutex, mais il est une autre caractéristique qui devient possible à travers la notion de propriété).

Si vous vous reportez à la classique VxWorks RTOS noyau, ils définissent trois mécanismes:

  • mutex - prend en charge la récursivité, et éventuellement d'héritage de priorité
  • sémaphore binaire - pas de récursivité, pas d'héritage, simple d'exclusion, de preneur et donneur n'a pas à être même thread, autorisation de diffusion disponibles
  • comptage sémaphore - pas de récursivité ou d'héritage, agit comme un ensemble cohérent de ressources compteur de n'importe quel comptage initial, les threads seul bloc où comptage net à l'encontre de la ressource est égale à zéro.

Encore une fois, cela varie quelque peu par la plate - forme et surtout de ce qu'ils appellent ces choses, mais ce doit être représentative des concepts et des différents mécanismes en jeu.

119voto

Jonathan Points 1032

La réponse est pas de l'efficacité. Non réentrant mutex conduire à un code de meilleure qualité.

Exemple: A::foo() acquiert le verrou. Il appelle ensuite B::bar(). Cela a bien fonctionné quand vous l'avez écrit. Mais quelque temps plus tard, quelqu'un change B::bar() pour appeler Un::baz(), qui acquiert également la serrure.

Eh bien, si vous n'avez pas récursive mutex, ce blocages. Si vous ne les avez, il fonctionne, mais il peut se casser. A::foo() peut avoir laissé l'objet dans un état incohérent avant l'appel à la barre (de), sur l'hypothèse que baz() n'a pas pu se faire parce qu'il acquiert également le mutex. Mais il est préférable de ne pas l'exécuter! La personne qui A écrit::foo() suppose que personne ne pouvait appeler Un::baz() en même temps - c'est toute la raison que ces deux méthodes a acquis le verrou.

Le bon modèle mental pour l'utilisation de mutex: Le mutex protège un invariant. Lorsque le mutex est tenue, l'invariant peut changer, mais avant de libérer le mutex, l'invariant est rétablie. Réentrant les verrous sont dangereux, parce que la deuxième fois que vous acquérir le verrou vous ne pouvez pas être sûr que l'invariant est vrai plus.

Si vous êtes heureux avec réentrant les verrous, c'est seulement parce que vous n'avez pas eu de déboguer un problème comme ça avant. Java a non réentrant verrous de ces jours en java.util.de façon concomitante.serrures, par la manière.

88voto

Chris Cleeland Points 2199

Comme écrit par Dave Butenhof lui-même:

"Le plus grand de tous les grands problèmes avec récursive mutex est que ils vous encouragent à complètement perdre la trace de votre schéma de verrouillage et la portée. C'est mortel. Mal. C'est le "fil eater". Vous détenez des serrures pour absolument plus brefs délais. Période. Toujours. Si vous êtes d'appel quelque chose avec un verrou détenu tout simplement parce que vous ne savez pas qu'il est occupé, ou parce que vous ne savez pas si le destinataire de l'appel des besoins le mutex, alors vous êtes le tenir trop longtemps. Vous visez un fusil de chasse à votre demande et tirer sur la gâchette. Vous avez sans doute commencé à utiliser les threads pour obtenir la simultanéité; mais vous avez juste a EMPÊCHÉ la concurrence."

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X