Informatik

Mutex einfach erklärt: Gemeinsame Daten schützen

Mutex einfach erklärt: Gemeinsame Daten schützen
Mutex einfach erklärt: Gemeinsame Daten schützen
Für Quiz, Lückentext, Lernkarten und Fortschritt ist JavaScript nötig. Alle Inhalte und Lösungen bleiben direkt lesbar.

Ein Mutex sorgt dafür, dass höchstens ein Thread oder Prozess gleichzeitig einen geschützten Codeabschnitt ausführt. So können mehrere nebenläufige Abläufe dieselben Daten verwenden, ohne ihre Änderungen unkontrolliert zu vermischen.

Du lernst, eine Race Condition zu erkennen, einen kritischen Abschnitt passend zu schützen und typische Mutex-Fehler zu vermeiden.

Deine Lernziele

Hake ab, was du schon kannst — und komm am Ende hierher zurück!

Warum gemeinsame Daten Schutz brauchen

Stell dir vor, zwei Threads kopieren fast gleichzeitig jeweils 100 Bytes in denselben globalen Speicherbereich. Das Kopieren besteht aus mehreren Einzelschritten und kann deshalb unterbrochen werden:

  1. Thread A beginnt mit seiner Kopie.
  2. Der Scheduler unterbricht A mitten im Kopiervorgang.
  3. Thread B schreibt seine Daten in denselben Speicherbereich.
  4. Danach setzt A seinen begonnenen Vorgang fort.

Das Ergebnis kann Teile aus beiden Datenpaketen enthalten. Die Reihenfolge der Threads hat also den Endzustand beeinflusst.

Definition

Race Condition

Eine Race Condition ist ein Fehler, bei dem das Ergebnis eines nebenläufigen Programms davon abhängt, in welcher zeitlichen Reihenfolge sich mehrere Zugriffe überschneiden.

Nicht jeder gleichzeitige Zugriff ist problematisch. Zwei Threads dürfen beispielsweise häufig dieselben unveränderten Daten lesen. Schutz wird nötig, wenn mindestens ein Zugriff Daten verändert und mehrere zusammengehörige Schritte nicht unkoordiniert verschränkt werden dürfen.

Teste dich
Frage 1 von 1LeichtWann besteht im Kopierbeispiel die Gefahr einer Race Condition?
Lösung: Wenn beide Threads denselben Speicherbereich verändern und ihre Einzelschritte verschränkt werden können — Eine Race Condition entsteht hier durch konkurrierende Änderungen an derselben Ressource und eine ungünstige zeitliche Verschachtelung.
So schützt ein Mutex den kritischen Abschnitt
Definition

Kritischer Abschnitt

Ein kritischer Abschnitt ist der Teil eines Programms, in dem gemeinsam genutzte Daten so bearbeitet werden, dass kein konkurrierender Zugriff dazwischenkommen darf.

Definition

Mutex

Ein Mutex ist ein Synchronisationsmechanismus für wechselseitigen Ausschluss. Wer ihn erfolgreich sperrt, erhält exklusiven Zugang zum zugehörigen kritischen Abschnitt. Andere beteiligte Threads oder Prozesse müssen warten oder den Zugriff später erneut versuchen.

Das Grundmuster besteht aus drei Schritten:

  1. Den Mutex unmittelbar vor dem kritischen Abschnitt sperren.
  2. Die zusammengehörige Operation vollständig ausführen.
  3. Den Mutex zuverlässig wieder freigeben.

Beim Kopierbeispiel umfasst der kritische Abschnitt den gesamten Kopiervorgang. Würde der Mutex schon nach dem ersten Teil freigegeben, könnte weiterhin eine Mischung entstehen.

Beispiel

Thread A sperrt den Mutex und kopiert sein vollständiges Datenpaket. Währenddessen erreicht Thread B dieselbe Stelle, kann den belegten Mutex aber nicht erwerben und wartet. Erst nachdem A den Mutex freigegeben hat, darf B sein vollständiges Paket kopieren. Zu jedem Zeitpunkt verändert somit höchstens einer der beiden Threads den Speicherbereich.

Alle Zugriffsstellen auf die geschützte Ressource müssen denselben Mutex beachten. Ein einzelner ungeschützter Schreibzugriff kann die Schutzwirkung umgehen.

Merke

Schütze nicht bloß eine einzelne Anweisung, sondern genau die zusammengehörige Operation, die gegenüber konkurrierenden Zugriffen ungeteilt bleiben muss.

Lückentext

Wähle in jeder Lücke die passende Form und prüfe anschließend deine Antworten.

Vor dem kritischen Abschnitt wird der Mutex . Während der Bearbeitung besitzt ein Thread . Nach der Bearbeitung muss er den Mutex . Andere Zugriffsstellen müssen verwenden.

Lösungen: Lücke 1: gesperrt; Lücke 2: exklusiven Zugriff; Lücke 3: freigeben; Lücke 4: denselben Mutex. Richtig ist die Reihenfolge Sperren, exklusiv bearbeiten und Freigeben. Schutz entsteht nur, wenn alle beteiligten Zugriffe dieselbe Sperre beachten.
Mutex, Semaphor und Synchronisation unterscheiden

Ein Mutex koordiniert Threads oder Prozesse für einen begrenzten Zugriff auf eine gemeinsame Ressource. Damit ist er ein Mittel der Synchronisation. Er legt aber nicht automatisch den gesamten Ablauf des Programms fest.

Bei typischen Mutex-Implementierungen gehört die Sperre ihrem Besitzer: Nur der Thread oder Prozess, der den Mutex erworben hat, darf ihn wieder freigeben.

Ein Semaphor verwaltet dagegen einen Zähler für verfügbare Berechtigungen oder Ressourcen. Ein binäres Semaphor hat nur zwei Zählerzustände und kann ähnlich wie eine Sperre wirken. Dennoch bleibt ein wichtiger Unterschied: Seine Freigabe ist grundsätzlich nicht in derselben Weise an den Erwerber gebunden.

FrageMutexSemaphor
Wie viele dürfen den geschützten Bereich gleichzeitig betreten?Höchstens einerBis zur festgelegten Anzahl
Welcher Zustand wird verwaltet?Gesperrt oder freiZähler verfügbarer Berechtigungen
Ist die Freigabe typischerweise an den Besitzer gebunden?JaNein

Konkrete Bibliotheken können Begriffe unterschiedlich verwenden. Entscheidend ist deshalb auch die dokumentierte Bedeutung des jeweiligen Sperrobjekts.

Teste dich
Frage 1 von 2LeichtWelche Aussage beschreibt den typischen Besitzunterschied richtig?
Lösung: Einen Mutex gibt normalerweise sein Besitzer frei; ein Semaphor ist nicht in gleicher Weise an einen Besitzer gebunden. — Ein Mutex verbindet exklusiven Zugriff gewöhnlich mit Besitz. Ein Semaphor verwaltet dagegen Berechtigungen über einen Zähler.
Frage 2 von 2MittelDu möchtest drei gleichartige Ressourcen gleichzeitig an höchstens drei Threads vergeben. Was passt grundsätzlich besser?
Lösung: Ein Semaphor mit drei verfügbaren Berechtigungen — Ein zählendes Semaphor kann eine festgelegte Zahl gleichzeitig verfügbarer Ressourcen abbilden.
Warten, Sperrdauer und Deadlocks beherrschen

Ist ein Mutex belegt, kann ein Thread unterschiedlich reagieren:

  • Beim passiven Warten versetzt ihn der Scheduler in einen Wartezustand und weckt ihn später wieder auf.
  • Beim aktiven Warten prüft er fortlaufend, ob die Sperre frei geworden ist. Eine solche Polling-Schleife wird bei Locks auch als Spinlock bezeichnet und kann Rechenzeit verbrauchen.
  • Bei einem späteren Versuch erledigt der Thread zunächst andere Arbeit.
  • Ein veralteter Wert kann verworfen werden, wenn ein neuerer Wert ihn ohnehin ersetzen wird und das Programm dies ausdrücklich erlaubt.

Welche Reaktion geeignet ist, hängt von Laufzeitumgebung, Scheduler und Aufgabe ab. Unkontrolliertes, häufiges Polling ist keine allgemeine Standardlösung.

Definition

Deadlock

Ein Deadlock oder eine Verklemmung liegt vor, wenn sich beteiligte Threads dauerhaft gegenseitig blockieren und keiner mehr fortfahren kann.

Ein typischer Deadlock entsteht so:

  1. Thread A hält Mutex 1 und wartet auf Mutex 2.
  2. Thread B hält Mutex 2 und wartet auf Mutex 1.
  3. Keiner kann seine erste Sperre freigeben, weil beide auf die zweite warten.

Halte Sperren deshalb nur so lange wie nötig. Wenn mehrere Sperren gemeinsam benötigt werden, hilft eine einheitliche Erwerbsreihenfolge, gegenseitiges Warten zu vermeiden. Bereichsgebundene Sperrobjekte können außerdem dafür sorgen, dass eine Sperre auch beim vorzeitigen Verlassen eines Codebereichs freigegeben wird.

Gut zu wissen

Bei einer Prioritätsinversion wartet ein hochpriorisierter Thread auf eine Ressource, die ein niedrigpriorisierter Thread hält. Ein Thread mittlerer Priorität kann den Besitzer zusätzlich verdrängen und dadurch den hochpriorisierten Thread indirekt länger blockieren. Geeignete Laufzeit- oder Betriebssystemmechanismen müssen diesen Sonderfall berücksichtigen.

Teste dich
Frage 1 von 2MittelThread A hält Sperre 1 und wartet auf Sperre 2. Thread B hält Sperre 2 und wartet auf Sperre 1. Was ist das zentrale Problem?
Lösung: Beide Threads können sich dauerhaft gegenseitig blockieren. — Das kreisförmige Warten bildet einen typischen Deadlock. Eine einheitliche Sperrreihenfolge kann diese Situation verhindern.
Frage 2 von 2SchwerEin zeitkritischer Thread darf seine Hauptaufgabe nicht anhalten, kann eine Zusatzinformation aber später erneut ablegen. Welche Reaktion ist sinnvoll?
Lösung: Die Zusatzoperation verschieben und den Mutex in einem späteren Zyklus erneut versuchen — Wenn die Zusatzoperation aufschiebbar ist, kann der Thread seine Hauptaufgabe fortsetzen und den geschützten Zugriff später erneut versuchen.
Mutex-Code richtig entwerfen und testen

Eine gute Sperre schützt alle Schritte, die gemeinsam konsistent bleiben müssen. Sie sollte aber nicht mehr Code umfassen als nötig. Diese Ausdehnung heißt Granularität des kritischen Abschnitts.

Ein zu kleiner Bereich lässt möglicherweise einen Teil der Operation ungeschützt. Ein unnötig großer Bereich blockiert andere Threads länger und kann die Leistung verschlechtern oder Verklemmungen begünstigen.

Beispiel

Eine Methode berechnet zuerst ausschließlich mit lokalen Daten und schreibt danach ein Ergebnis in eine gemeinsame Liste. Der Mutex muss den gemeinsamen Listen-Zugriff schützen. Die vorherige lokale Berechnung kann außerhalb des kritischen Abschnitts bleiben, sofern sie keine gemeinsam veränderlichen Daten verwendet.

Nebenläufigkeitsfehler treten oft nur bei seltenen zeitlichen Abläufen auf. Ein einziger erfolgreicher Praxistest beweist deshalb keine Fehlerfreiheit. Sinnvoll ist eine Kombination aus:

  • Modultests, die an kritischen Stellen gezielt Threadwechsel provozieren;
  • Praxistests unter wechselnden Betriebsbedingungen;
  • Codereviews, die alle Zugriffe auf die gemeinsame Ressource darauf prüfen, ob derselbe Mutex konsequent verwendet wird.
Teste dich
Frage 1 von 2MittelWelche Teststrategie prüft den Mutex am überzeugendsten?
Lösung: Kritische Threadwechsel gezielt auslösen und zusätzlich alle Zugriffsstellen im Review kontrollieren — Gezielte Verschachtelungen machen Race Conditions wahrscheinlicher sichtbar. Ein Review findet zusätzlich vergessene Sperren, die ein Testlauf möglicherweise nie erreicht.
Frage 2 von 2SchwerEine lokale Berechnung dauert lange; nur das abschließende Einfügen in eine gemeinsame Liste verändert geteilte Daten. Welche Granularität ist begründet?
Lösung: Nur den konsistenzkritischen Listen-Zugriff sperren — Der Mutex gehört um die zusammengehörige Änderung der gemeinsamen Liste. Rein lokale Vorarbeit benötigt diesen Schutz nicht.
Karteikasten
Karteikasten

Überlege zuerst selbst und drehe die Karte anschließend zum Prüfen um.

Alles auf einen Blick
Mindmap
  • Mutex
    • Ziel: gemeinsame Daten konsistent halten
    • Gefahr ohne Schutz: Race Condition
    • Schutzbereich: kritischer Abschnitt
    • Ablauf: sperren, bearbeiten, freigeben
    • Besitz: Freigabe durch den Erwerber
    • Warten: passiv, aktiv oder späterer Versuch
    • Risiko bei Fehlgebrauch: Deadlock
    • Prüfung: Threadwechsel, Praxistest und Codereview
Abschluss-Check
Teste dich
Frage 1 von 3LeichtWas garantiert ein korrekt verwendeter Mutex für seinen kritischen Abschnitt?
Lösung: Höchstens ein beteiligter Thread oder Prozess führt ihn gleichzeitig aus. — Der Mutex stellt wechselseitigen Ausschluss für den zugeordneten kritischen Abschnitt her.
Frage 2 von 3MittelZwei Threads verändern dieselbe Datenstruktur. Einer verwendet den Mutex, der andere nicht. Ist die Ressource zuverlässig geschützt?
Lösung: Nein, weil alle konkurrierenden Zugriffsstellen dieselbe Sperre beachten müssen. — Eine vergessene Sperre kann den Schutz umgehen. Deshalb gehören alle Zugriffsstellen in Test und Codereview.
Frage 3 von 3SchwerZwei Sperren werden in mehreren Programmteilen benötigt. Welche Regel senkt das Deadlock-Risiko?
Lösung: Die Sperren überall in derselben festgelegten Reihenfolge erwerben. — Eine gemeinsame Erwerbsreihenfolge verhindert den typischen Kreis, bei dem jeder Thread eine Sperre hält und auf die andere wartet.

Prüfe zum Schluss deinen Denkweg: Kannst du bei einem nebenläufigen Zugriff die gemeinsam genutzte Ressource benennen, den vollständigen kritischen Abschnitt markieren und erklären, warum die gewählte Sperre weder zu klein noch unnötig groß ist? Dann kannst du einen Mutex begründet einsetzen.

Passend dazu