Race Condition einfach erklärt

Race Condition einfach erklärt
Race Condition einfach erklärt
Für Quiz, Lückentext, Lernkarten und Fortschritt ist JavaScript nötig. Alle Inhalte und Lösungen bleiben direkt lesbar.

Eine Race Condition entsteht, wenn mehrere Akteure auf eine gemeinsame Ressource zugreifen und das Ergebnis von ihrer zeitlichen Reihenfolge abhängt. Fehlt eine wirksame Koordination, können beispielsweise Änderungen verloren gehen oder Entscheidungen auf einem veralteten Zustand beruhen.

Auf dieser Seite lernst du, solche Wettlaufsituationen in Abläufen zu erkennen, ihre Ursache zu erklären und passende Gegenmaßnahmen auszuwählen.

Deine Lernziele

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

Woran erkennst du eine Race Condition?

Stell dir vor, zwei Kunden wollen gleichzeitig den letzten freien Kinoplatz kaufen. Beide sehen zunächst „frei“. Wenn Prüfung und Buchung nicht zusammen geschützt sind, können beide Käufe bestätigt werden.

Typischerweise kommen vier Merkmale zusammen:

  1. Mehrere Akteure, zum Beispiel Threads, Prozesse oder Dienste, arbeiten nebenläufig.
  2. Sie greifen auf dieselbe Ressource zu, etwa eine Variable, Datei oder einen Datensatz.
  3. Mindestens ein Zugriff verändert den Zustand.
  4. Die Zugriffe sind nicht ausreichend geordnet oder synchronisiert.
Definition

Nebenläufigkeit

Mehrere Abläufe machen im selben Zeitraum Fortschritt. Ihre einzelnen Schritte können sich zeitlich verschachteln. Sie müssen dafür nicht exakt gleichzeitig auf verschiedenen Prozessorkernen laufen.

Definition

Race Condition

Eine Race Condition ist eine Situation, in der das Ergebnis von der Reihenfolge oder dem Timing nebenläufiger Ereignisse abhängt.

Ein Data Race ist enger gefasst: Zwei Threads greifen gleichzeitig auf dieselbe Speicherstelle zu, mindestens einer davon schreibend. Nicht jede allgemeine Race Condition muss ein solcher gleichzeitiger Speicherzugriff sein. Auch ein getrenntes „prüfen, dann handeln“ kann eine Race Condition erzeugen.

Teste dich
Frage 1 von 1LeichtWelche Situation enthält alle typischen Voraussetzungen einer Race Condition?
Lösung: Zwei Threads verändern dieselbe Variable ohne ausreichende Koordination. — Mehrere Akteure müssen auf gemeinsamen Zustand einwirken, und das Ergebnis muss von ihrer zeitlichen Ordnung abhängen können.
Wie geht beim Read-Modify-Write ein Update verloren?

Viele scheinbar einfache Änderungen bestehen aus drei Schritten:

  1. Read: alten Wert lesen,
  2. Modify: neuen Wert lokal berechnen,
  3. Write: neuen Wert zurückschreiben.

Diese Folge heißt Read-Modify-Write. Wird sie unterbrochen, kann ein anderer Akteur denselben alten Wert lesen.

Beispiel

A und B sollen einen gemeinsamen Wert jeweils um 1 erhöhen. Der Ausgangswert ist 1. Nach zwei vollständigen Erhöhungen wird $1+1+1=3$ erwartet.

Geordneter Ablauf:

text A liest 1 → berechnet 2 → schreibt 2 B liest 2 → berechnet 3 → schreibt 3 Endwert: 3

Überlappender Ablauf:

text A liest 1 B liest 1 A berechnet lokal 2 B berechnet lokal 2 A schreibt 2 B schreibt 2 Endwert: 2

Beide Akteure berechnen aus demselben alten Wert die 2. Der letzte Schreibzugriff speichert deshalb ebenfalls 2. Eine Erhöhung ist verloren gegangen.

Definition

Kritischer Abschnitt

Ein kritischer Abschnitt ist der Teil eines Ablaufs, der auf gemeinsamen veränderlichen Zustand zugreift und deshalb vor störender Überlappung geschützt werden muss. Im Beispiel gehören Lesen, Erhöhen und Zurückschreiben gemeinsam dazu.

Merke

Nicht die Anzahl der ausgeführten Befehle entscheidet über den Endwert, sondern ihre mögliche Verschachtelung.

Teste dich
Frage 1 von 1MittelA liest den Wert 1. Danach liest B ebenfalls 1, bevor jemand zurückschreibt. Was ist bereits klar?
Lösung: Beide Akteure berechnen ihre Erhöhung aus demselben alten Zustand. — Ohne eine ausdrücklich vorgesehene Koordination darfst du kein automatisches Warten annehmen. Beide Schreibzugriffe können denselben neuen Wert speichern.
Warum ist Check-Then-Act ebenfalls gefährlich?

Beim Muster Check-Then-Act wird zuerst ein Zustand geprüft und danach aufgrund des Ergebnisses gehandelt. Zwischen diesen Schritten liegt ein Zeitfenster.

Beispiel

Ein Buchungssystem arbeitet so:

```text

`

  1. Prüfe: Ist Platz 42 frei?
  2. Wenn ja: Buche Platz 42.

Anfrage A prüft den Platz und erhält „frei“. Bevor A bucht, prüft auch Anfrage B und erhält ebenfalls „frei“. Beide handeln nun aufgrund derselben veralteten Beobachtung.

Die fachliche Regel „Ein Platz darf höchstens einmal vergeben werden“ kann verletzt werden, obwohl jede Anfrage für sich logisch aussieht.

Dieses Problem tritt auch bei Berechtigungsprüfungen, Dateien oder Kontoständen auf. Entscheidend ist stets: Darf sich der geprüfte Zustand vor der anschließenden Handlung noch ändern?

Teste dich
Frage 1 von 1MittelWelche Änderung beseitigt im Platzbeispiel das gefährliche Zeitfenster am direktesten?
Lösung: Prüfung und Buchung werden als eine geschützte, unteilbare Aktion behandelt. — Prüfung und Handlung müssen gemeinsam koordiniert werden. Logging kann die Diagnose unterstützen, ersetzt aber keine Schutzmaßnahme.
Wie schützt du den kritischen Abschnitt?

Es gibt nicht eine einzige Lösung für alle Systeme. Wähle die Maßnahme passend zur Ressource und zur geforderten Invariante. Eine Invariante ist eine Bedingung, die trotz nebenläufiger Abläufe erhalten bleiben muss, zum Beispiel: „Ein Platz wird höchstens einmal vergeben.“

Gemeinsamen Zustand vermeiden

Wenn Akteure getrennte, unveränderliche Daten verwenden, können sie sich nicht durch Schreibzugriffe auf denselben Zustand stören. Auch ein eindeutiger Besitzer einer Ressource kann Änderungen geordnet ausführen.

Operation atomar ausführen

Eine atomare Operation erscheint anderen Akteuren als unteilbare Einheit. Für den gemeinsamen Zähler muss die gesamte Änderung geschützt sein, nicht nur das Zurückschreiben.

Mit einer Sperre koordinieren

Ein Lock oder Mutex gewährt jeweils nur einem Akteur Zugang zum geschützten Abschnitt. Erst nach der Freigabe darf der nächste Akteur eintreten.

text Lock anfordern Wert lesen Wert erhöhen Wert schreiben Lock freigeben

Die Sperre muss bei allen Zugriffspfaden gelten, die dieselbe Invariante beeinflussen. Ein geschützter Schreibpfad hilft wenig, wenn ein anderer Pfad den Zustand ungeschützt liest und daraus eine Entscheidung ableitet.

Konflikte über Versionen erkennen

Bei einem Datensatz kann eine Aktualisierung nur dann angenommen werden, wenn seine Version noch der zuvor gelesenen Version entspricht. Hat ein anderer Akteur inzwischen geschrieben, wird der Konflikt erkannt. Der Vorgang kann den neuen Zustand lesen und kontrolliert wiederholt werden.

Gut zu wissen

Synchronisierung hat Kosten: Zu große geschützte Bereiche können andere Akteure unnötig lange blockieren. Zu kleine Bereiche lassen zusammengehörige Schritte ungeschützt. Die richtige Granularität umfasst genau die Operationen, die gemeinsam die Invariante sichern müssen.

Teste dich
Frage 1 von 1SchwerEin Programm schützt das Schreiben eines Kontostands, liest den Saldo aber ungeschützt, um eine Auszahlung zu erlauben. Was ist die treffendste Beurteilung?
Lösung: Die Synchronisierung ist unvollständig, weil Lesen, Entscheidung und Änderung zusammengehören. — Geschützt werden muss die entscheidungsrelevante Folge. Paralleles Lesen kann unproblematisch sein, solange niemand den beobachteten Zustand während dieser Entscheidung unkontrolliert verändert.
Wie findest und testest du Race Conditions?

Race Conditions sind oft schwer reproduzierbar. Last, Hardware und Ablaufplanung beeinflussen die Verschachtelung. Sogar zusätzliches Logging kann das Timing verändern und ein beobachtetes Symptom verschwinden lassen.

Ein sinnvolles Vorgehen ist:

  1. Gemeinsame veränderliche Ressourcen und alle Zugriffspfade erfassen.
  2. Die zu erhaltenden Invarianten benennen.
  3. Mögliche Lese-, Änderungs- und Schreibfolgen als Zeitabläufe notieren.
  4. Hohe Konkurrenz erzeugen und Timings kontrolliert variieren.
  5. Zugriffe mit Ressourcenkennungen protokollieren und die Reihenfolge rekonstruieren.
  6. Gefundene Fehler durch echte Koordination beheben und erneut testen.

Statische Analyse untersucht Code ohne Ausführung, kann aber auch vor Abläufen warnen, die praktisch keinen Fehler erzeugen. Dynamische Analyse beobachtet ausgeführte Abläufe, kann jedoch eine Race Condition übersehen, wenn genau die problematische Verschachtelung im Test nicht auftritt.

Merke

Ein erfolgreicher Test beweist nicht, dass keine Race Condition existiert. Er zeigt nur, dass der Fehler in den beobachteten Abläufen nicht aufgetreten ist.

sleep oder eine feste Verzögerung ist keine verlässliche Reparatur. Unter anderer Last kann sich die Reihenfolge erneut ändern.

Teste dich
Frage 1 von 1MittelEin Konkurrenztest läuft hundertmal ohne Fehler durch. Welche Schlussfolgerung ist zulässig?
Lösung: In diesen Testläufen wurde keine fehlerhafte Verschachtelung beobachtet. — Nebenläufigkeit erzeugt viele mögliche Abläufe. Tests liefern Hinweise, müssen aber durch Analyse der Ressourcen, Invarianten und Schutzmechanismen ergänzt werden.
Karteikasten
Karteikasten

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

Alles auf einen Blick
Mindmap
  • Race Condition
    • Voraussetzungen
      • mehrere Akteure
      • gemeinsame Ressource
      • mindestens eine Änderung
      • fehlende Koordination
    • typische Muster
      • Read-Modify-Write
      • Check-Then-Act
    • mögliche Folgen
      • verlorene Updates
      • inkonsistente Daten
      • schwer reproduzierbare Fehler
    • Gegenmaßnahmen
      • Zustand vermeiden oder eindeutig besitzen
      • atomare Operationen verwenden
      • kritische Abschnitte sperren
      • Versionskonflikte erkennen
    • Prüfung
      • Abläufe modellieren
      • Konkurrenz und Timing variieren
      • Invarianten kontrollieren
Abschluss-Check
Teste dich
Frage 1 von 3LeichtWodurch wird eine Race Condition wesentlich bestimmt?
Lösung: Durch die Abhängigkeit des Ergebnisses von Timing oder Reihenfolge konkurrierender Zugriffe. — Das Kernmerkmal ist die Reihenfolgeabhängigkeit bei unzureichend koordiniertem Zugriff auf gemeinsamen Zustand.
Frage 2 von 3MittelZwei Threads erhöhen einen gemeinsamen Wert. Beide lesen 1, berechnen 2 und schreiben 2. Warum lautet das Ergebnis nicht 3?
Lösung: Beide Schreibzugriffe beruhen auf demselben alten Wert; eine Änderung wird überschrieben. — Der Fehler liegt nicht in der Addition, sondern in der ungeschützten Read-Modify-Write-Folge.
Frage 3 von 3SchwerEin Ticketsystem prüft „letztes Ticket verfügbar“ und verkauft es anschließend. Welche Begründung für die Schutzmaßnahme ist fachlich vollständig?
Lösung: Prüfung und Verkauf müssen gemeinsam koordiniert werden, damit die Invariante „höchstens ein Verkauf“ erhalten bleibt. — Bestimme zuerst die Invariante und schütze dann alle Schritte, deren Verschachtelung sie verletzen könnte.

Prüfe deinen Denkweg abschließend mit drei Fragen: Welche Ressource wird geteilt? Welche Schritte dürfen nicht störend verschachtelt werden? Welche Maßnahme erhält die geforderte Invariante?

Passend dazu