wake-up-neo.com

Wann rekursives Mutex verwenden?

Ich verstehe, dass rekursiver Mutex das mehrfache Sperren von Mutex ermöglicht, ohne dass es zu einem Deadlock kommt, und dass er genauso oft entsperrt werden sollte. Aber in welchen speziellen Situationen müssen Sie einen rekursiven Mutex verwenden? Ich suche Situationen auf Design-/Code-Ebene.

55
jasonline

Zum Beispiel, wenn Sie eine Funktion haben, die sie rekursiv aufruft, und Sie einen synchronisierten Zugriff darauf erhalten möchten:

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

ohne einen rekursiven Mutex müssten Sie zuerst eine "Einstiegspunkt" -Funktion erstellen, und dies wird umständlich, wenn Sie eine Reihe von Funktionen haben, die gegenseitig rekursiv sind. Ohne rekursiven Mutex:

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

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

Rekursive und nicht-rekursive Mutexe haben verschiedene Anwendungsfälle. Kein Mutex-Typ kann den anderen leicht ersetzen. Nicht-rekursive Mutexe haben weniger Overhead und rekursive Mutexe haben in einigen Situationen nützliche oder sogar benötigte Semantik und in anderen Situationen gefährliche oder sogar gebrochene Semantik. In den meisten Fällen kann jemand eine Strategie mit rekursiven Mutexen durch eine andere sicherere und effizientere Strategie ersetzen, die auf der Verwendung von nicht rekursiven Mutexen basiert.

  • Wenn Sie nur andere Threads von der Verwendung Ihrer durch Mutex geschützten Ressource ausschließen möchten, können Sie einen beliebigen Mutex verwenden, möchten jedoch möglicherweise den nicht rekursiven Mutex verwenden, da dieser einen geringeren Overhead hat.
  • Wenn Sie Funktionen rekursiv aufrufen möchten, die denselben Mutex sperren, dann können Sie entweder
    • verwenden Sie ein rekursiver Mutex oder
    • denselben nicht-rekursiven Mutex immer wieder entsperren und sperren müssen (Vorsicht vor gleichzeitigen Threads!) (vorausgesetzt, dies ist semantisch korrekt, es könnte sich dennoch um ein Leistungsproblem handeln), oder
    • müssen irgendwie kommentieren, welche Mutexe sie bereits gesperrt haben (simulieren rekursiven Besitz/Mutexe).
  • Wenn Sie mehrere mutexgeschützte Objekte aus einer Gruppe solcher Objekte sperren möchten, bei denen die Gruppen möglicherweise durch Zusammenführen erstellt wurden, können Sie Auswählen.
    • pro Objekt genau zu verwenden ein Mutex, damit mehr Threads parallel arbeiten können, oder
    • zu verwenden pro Objekt eine Referenz zu jedem möglicherweise gemeinsam genutzten rekursiven Mutex, um die Wahrscheinlichkeit von nicht alle Mutexe zusammen sperren, oder
    • zu verwenden pro Objekt eine vergleichbare Referenz zu jedem möglicherweise gemeinsam genutzten nicht-rekursiven Mutex, Umgehung des Absicht, mehrere Male zu sperren.
  • Wenn Sie eine Sperre in einem anderen Thread aufheben möchten, als sie gesperrt wurde, müssen Sie nicht-rekursive Sperren verwenden (oder rekursive Sperren, die dies explizit zulassen, anstatt Ausnahmen auszulösen).
  • Wenn Sie Synchronisationsvariablen verwenden möchten , müssen Sie in der Lage sein, den Mutex explizit zu entsperren sein, während Sie auf eine Synchronisationsvariable warten , damit die Ressource in anderen Threads verwendet werden darf. Dies ist nur mit nicht rekursive Mutexe möglich, da rekursive Mutexe bereits vom Aufrufer der aktuellen Funktion gesperrt worden sein könnten.
23
comonad

Wenn Sie ein Codebeispiel sehen möchten, das rekursive Mutexe verwendet, lesen Sie die Quellen für "Electric Fence" für Linux/Unix. Es war eines der gängigen Unix-Tools zum Auffinden von "Grenzen prüfen" von Lese-/Schreibüberschreitungen und -unterschreitungen sowie zum Verwenden von freigemachtem Speicher, bevor Valgrind auf den Markt kam.

Kompilieren und verknüpfen Sie einfach electric fence mit sources (Option -g mit gcc/g ++) und verknüpfen Sie es dann mit Ihrer Software mit der Verknüpfungsoption -lefence und beginnen Sie, die Aufrufe von malloc/free durchzugehen. http://elinux.org/Electric_Fence

3
FormerAtariUser

Ich bin heute auf die Notwendigkeit eines rekursiven Mutex gestoßen, und ich denke, es ist das vielleicht einfachste Beispiel unter den bisher veröffentlichten Antworten: Dies ist eine Klasse, die zwei API-Funktionen bereitstellt, Process (...) und reset ().

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

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

Beide Funktionen dürfen nicht gleichzeitig ausgeführt werden, da sie Interna der Klasse ändern. Daher wollte ich einen Mutex verwenden. Das Problem ist, dass Process () intern reset () aufruft und einen Deadlock erzeugt, da mMutex bereits erfasst wurde. Das Sperren mit einer rekursiven Sperre behebt das Problem.

3
Doktor Schrott

Es wäre sicherlich ein Problem, wenn ein blockierter Thread versuchen würde, (erneut) einen Mutex zu erhalten, den er bereits besaß ...

Gibt es einen Grund, nicht zuzulassen, dass ein Mutex mehrmals von demselben Thread abgerufen wird?

2
Michael Burr