web-dev-qa-db-fra.com

Quand utiliser un mutex récursif?

Je comprends que le mutex récursif permet au mutex d'être verrouillé plus d'une fois sans arriver à une impasse et devrait être déverrouillé le même nombre de fois. Mais dans quelles situations spécifiques avez-vous besoin d'utiliser un mutex récursif? Je recherche des situations de conception/niveau de code.

55
jasonline

Par exemple, lorsque vous avez une fonction qui l'appelle récursivement et que vous souhaitez obtenir un accès synchronisé à celle-ci:

void foo() {
   ... mutex_acquire();
   ... foo();
   ... mutex_release();
}

sans mutex récursif, vous devez d'abord créer une fonction de "point d'entrée", ce qui devient lourd lorsque vous disposez d'un ensemble de fonctions mutuellement récursives. Sans mutex récursif:

void foo_entry() {
   mutex_acquire(); foo(); mutex_release(); }

void foo() { ... foo(); ... }
48
Antti Huima

Les mutex récursifs et non récursifs ont différents cas d'utilisation. Aucun type de mutex ne peut facilement remplacer l'autre. Les mutex non récursifs ont moins de surcharge, et les mutex récursifs ont dans certaines situations une sémantique utile ou même nécessaire et dans d'autres situations une sémantique dangereuse ou même brisée. Dans la plupart des cas, quelqu'un peut remplacer toute stratégie utilisant des mutex récursifs par une stratégie différente, plus sûre et plus efficace, basée sur l'utilisation de mutex non récursifs.

  • Si vous souhaitez simplement exclure d'autres threads de l'utilisation de votre ressource protégée mutex, vous pouvez utiliser le type n'importe quel mutex, mais vous pouvez utiliser le mutex non récursif en raison de sa surcharge réduite.
  • Si vous voulez appeler des fonctions récursivement, qui verrouillent le même mutex, alors elles
    • doivent utiliser n mutex récursif, ou
    • devez déverrouiller et verrouiller le même mutex non récursif encore et encore (méfiez-vous des threads simultanés!) (en supposant que cela soit sémantique, cela pourrait toujours être un problème de performances), ou
    • doivent en quelque sorte annoter les mutex qu'ils ont déjà verrouillés (simulant la propriété/mutex récursive).
  • Si vous souhaitez verrouiller plusieurs objets protégés mutex à partir d'un ensemble de ces objets, là où les ensembles auraient pu être créés par fusion, vous pouvez choisir
    • d'utiliser exactement par objet n mutex, permettant à plus de threads de fonctionner en parallèle, ou
    • à utiliser par objet ne référence à n'importe quel éventuellement partagé mutex récursif, pour réduire la probabilité de ne pas verrouiller tous les mutex ensemble, ou
    • à utiliser par objet ne référence comparable à n'importe quel éventuellement partagé mutex non récursif, contournant le l'intention de verrouiller plusieurs fois.
  • Si vous souhaitez libérer un verrou dans un thread différent de celui qu'il a été verrouillé, vous devez utiliser des verrous non récursifs (ou des verrous récursifs qui autorisent explicitement cela au lieu de lever des exceptions).
  • Si vous souhaitez utiliser des variables de synchronisation , alors vous devez être capable de déverrouiller explicitement le mutex en attendant une variable de synchronisation , afin que la ressource soit autorisée à être utilisée dans d'autres threads. Cela n'est parfaitement possible qu'avec mutex non récursifs, car les mutex récursifs pourraient déjà avoir été verrouillés par l'appelant de la fonction actuelle.
23
comonad

Si vous voulez voir un exemple de code qui utilise des mutex récursifs, regardez les sources de "Electric Fence" pour Linux/Unix. 'C'était l'un des outils Unix courants pour trouver des "dépassements et des sous-exécutions" de vérification des limites "en lecture/écriture ainsi que pour utiliser la mémoire qui a été libérée, avant Valgrind .

Compilez et liez simplement la clôture électrique avec les sources (option -g avec gcc/g ++), puis liez-la avec votre logiciel avec l'option de lien -lefence, et commencez à passer à travers les appels à malloc/free. http://elinux.org/Electric_Fence

3
FormerAtariUser

J'ai rencontré le besoin d'un mutex récursif aujourd'hui, et je pense que c'est peut-être l'exemple le plus simple parmi les réponses publiées jusqu'à présent: il s'agit d'une classe qui expose deux fonctions API, Process (...) et reset ().

public void Process(...)
{
  acquire_mutex(mMutex);
  // Heavy processing
  ...
  reset();
  ...
  release_mutex(mMutex);
}

public void reset()
{
  acquire_mutex(mMutex);
  // Reset
  ...
  release_mutex(mMutex);
}

Les deux fonctions ne doivent pas s'exécuter simultanément car elles modifient les internes de la classe, j'ai donc voulu utiliser un mutex. Le problème est que Process () appelle reset () en interne, et cela créerait un blocage car mMutex est déjà acquis. Les verrouiller avec un verrou récursif résout plutôt le problème.

3
Doktor Schrott

Ce serait certainement un problème si un thread bloqué essayait d'acquérir (encore) un mutex qu'il possédait déjà ...

Y a-t-il une raison pour ne pas permettre qu'un mutex soit acquis plusieurs fois par le même thread?

2
Michael Burr