Race Condition einfach erklärt
Du erkennst Race Conditions, verfolgst verlorene Änderungen und erklärst, wie Sperren schützen und was bestandene Tests aussagen.
Kurz gesagt
Eine Race Condition ist eine Wettlaufsituation: Das Ergebnis nebenläufiger Abläufe hängt von ihrer zeitlichen Reihenfolge ab. Dabei können Änderungen verloren gehen, wenn ein Ablauf mit einem veralteten Wert weiterarbeitet.
Zwei Abläufe können denselben gespeicherten Wert bearbeiten. Wechseln sich ihre Schritte ungünstig ab, kann eine Änderung die andere überschreiben. Du untersuchst das mit ausgedachten Ablaufmodellen: Sie beschreiben Einzelschritte und sind kein ausführbarer Programmcode.
Deine Lernziele
Eine Race Condition entsteht, wenn das Ergebnis gleichzeitig laufender Abläufe von ihrer zeitlichen Reihenfolge abhängt. Gleichzeitig laufend bedeutet hier nebenläufig: Die Abläufe sind im selben Zeitraum aktiv. Ihre Einzelschritte müssen nicht im exakt selben Augenblick stattfinden.
Wir nennen zwei Abläufe A und B. Jeder behält seine eigene Schrittfolge bei. Trotzdem können sich Schritte von A und B abwechseln. Diese Überlappung ihrer Schrittfolgen heißt Verschachtelung.
Im folgenden Modell gibt es eine gemeinsame Anzeige mit genau einem Textfeld. Jeder Schreibschritt ersetzt den bisherigen Text vollständig. Nach beiden Schreibschritten wird der Text abgelesen. Allein der Zugriff auf gemeinsame Daten reicht als Erklärung einer Race Condition noch nicht: Entscheidend ist, dass die Reihenfolge das Ergebnis beeinflusst.
Race Condition
Eine Wettlaufsituation, bei der das Ergebnis davon abhängt, in welcher zeitlichen Reihenfolge die Schritte nebenläufiger Abläufe ausgeführt werden.
Beispiel: Eine gemeinsame Anzeige (ausgedachtes Modell)
- 1A schreibt den Text „bereit“. B schreibt den Text „Pause“.
- 2Schreibt zuerst A und danach B, zeigt die Anzeige am Ende „Pause“.
- 3Schreibt zuerst B und danach A, zeigt die Anzeige am Ende „bereit“.
- 4Die Schreibreihenfolge verändert den abgelesenen Text. Damit liegt in diesem Modell eine Race Condition vor.
Teste dich
Ein Update ist eine Änderung eines gespeicherten Werts. Im ausgedachten Rechenmodell bearbeiten A und B einen gemeinsamen Punktestand. Er startet bei 12. A soll ihn um 4 erhöhen, B um 7. Beide Erhöhungen sollen genau einmal eingehen. Erwartet sind danach 23 Punkte.
Jede Erhöhung besteht im Modell aus drei Schritten: den gemeinsamen Wert lesen, mit dem gelesenen Wert rechnen und das Ergebnis zurückschreiben. Beim Lesen legt jeder Ablauf einen eigenen Zwischenwert an. Dieser lokale Wert gehört nur diesem Ablauf. Das Rechnen verändert zunächst diesen Zwischenwert; erst das Zurückschreiben verändert den gemeinsamen Punktestand. Schreibt der andere Ablauf inzwischen etwas zurück, ändert das einen bereits gelesenen Zwischenwert nicht.
Innerhalb jedes Ablaufs bleibt die Reihenfolge Lesen, Rechnen, Schreiben erhalten. Andere Zugriffe gibt es im Modell nicht. Die erste Tabelle zeigt A vollständig vor B. Die zweite zeigt eine Verschachtelung. „Kein Schritt“ bedeutet, dass der betreffende Ablauf in dieser Zeile keinen Schritt ausführt.
In der zweiten Tabelle rechnen beide mit 12. B schreibt zuletzt 19 und überschreibt damit As Ergebnis 16. As Erhöhung um 4 fehlt im Endwert. Das ist hier ein verlorenes Update. Das Beispiel zeigt eine mögliche fehlerhafte Reihenfolge; andere Reihenfolgen können zum erwarteten Ergebnis führen.
| Ablauf A | Ablauf B | Gemeinsamer Punktestand |
|---|---|---|
| Liest 12 | Kein Schritt | 12 |
| Erhöht seinen Zwischenwert auf 16 | Kein Schritt | 12 |
| Schreibt 16 zurück | Kein Schritt | 16 |
| Kein Schritt | Liest 16 | 16 |
| Kein Schritt | Erhöht seinen Zwischenwert auf 23 | 16 |
| Kein Schritt | Schreibt 23 zurück | 23 |
| Ablauf A | Ablauf B | Gemeinsamer Punktestand |
|---|---|---|
| Liest 12 | Kein Schritt | 12 |
| Kein Schritt | Liest 12 | 12 |
| Erhöht seinen Zwischenwert auf 16 | Kein Schritt | 12 |
| Kein Schritt | Erhöht seinen Zwischenwert auf 19 | 12 |
| Schreibt 16 zurück | Kein Schritt | 16 |
| Kein Schritt | Schreibt 19 zurück | 19 |
Teste dich
Im Rechenmodell muss die zusammengehörige Folge aus Lesen, Verändern und Zurückschreiben geschützt werden. Sie bildet hier den kritischen Abschnitt. Würde ein anderer Ablauf dazwischen denselben Wert bearbeiten, könnte er einen veralteten Zwischenwert verwenden.
Eine Sperre regelt in unserem Modell den Zugang: Höchstens einer der beiden Abläufe kann dieselbe Sperre zur selben Zeit besitzen. Der andere wartet vor dem geschützten Abschnitt. Er darf diesen erst beginnen, nachdem die Sperre freigegeben und von ihm erworben wurde.
Beide Abläufe müssen dieselbe Sperre vor dem Lesen erwerben und erst nach dem Zurückschreiben freigeben. Dann liest der zweite Ablauf den bereits aktualisierten gemeinsamen Wert. Unter diesen Voraussetzungen verhindert die Sperre im Rechenmodell verlorene Updates. Sie legt dabei nicht fest, welcher Ablauf zuerst an die Reihe kommt.
Eine Sperre nur um das Zurückschreiben reicht hier nicht: Beide Abläufe könnten vorher denselben alten Wert lesen. Verschiedene Sperren für A und B würden ebenfalls nicht verhindern, dass beide vor dem ersten Zurückschreiben denselben alten Wert lesen.
Kritischer Abschnitt
Hier die zusammengehörige Folge aus Lesen, Verändern und Zurückschreiben des gemeinsamen Werts, die gegen überlappende Zugriffe geschützt werden muss.
Merke: Im Rechenmodell schützt dieselbe Sperre beide Abläufe vom Lesen bis einschließlich zum Zurückschreiben. Der wartende Ablauf liest erst nach dem Erwerb der Sperre.
Beispiel: Zwei geschützte Erhöhungen (ausgedachtes Modell)
- 1Der gemeinsame Zähler startet bei 6. A erhöht um 3, B um 2. Beide benutzen dieselbe Sperre um Lesen, Rechnen und Schreiben.
- 2A erwirbt die Sperre. B versucht sie ebenfalls zu erwerben und wartet vor seinem Lesen.
- 3A liest 6, berechnet 9 und schreibt 9 zurück. Danach gibt A die Sperre frei.
- 4B erwirbt die Sperre, liest 9, berechnet 11 und schreibt 11 zurück. Danach gibt B die Sperre frei.
- 5Der Endwert 11 enthält beide Erhöhungen. Auch bei B vor A ergäbe sich hier 11.
Teste dich
Ein Test führt ein Programm mit bestimmten Eingaben aus und prüft dabei ein erwartetes Ergebnis. Ein bestandener Test zeigt hier, dass der beobachtete Ablauf den geprüften Endwert geliefert hat. Daraus folgt nicht, dass andere mögliche Schrittfolgen ebenfalls richtig enden.
Auch ohne Sperre können die Schritte zufällig günstig liegen: A kann seine Änderung abschließen, bevor B liest. Ein korrekter Endwert beweist deshalb auch nicht, dass eine wirksame Sperre vorhanden ist. Viele erfolgreiche Wiederholungen schließen eine Race Condition nicht aus, solange andere mögliche Reihenfolgen ungeprüft bleiben.
Zusätzliche Protokollausgaben, also Ausgaben über den Programmablauf, können das zeitliche Verhalten verändern. Dadurch können Fehlersymptome beim Testen verschwinden. Das Verschwinden ist kein Beweis, dass die Ursache behoben wurde.
Tests mit unterschiedlichen möglichen Reihenfolgen können Fehler sichtbar machen. Ein einzelner beobachteter Ablauf beweist aber keine allgemeine Fehlerfreiheit. Für das Sperrmodell muss zusätzlich begründet werden, warum beide Abläufe dieselbe Sperre um die gesamte Folge aus Lesen, Rechnen und Schreiben verwenden.
Beispiel: Ein günstiger Testlauf (ausgedachtes Modell)
- 1Ein gemeinsamer Vorratszähler startet bei 40. A erhöht um 6, B um 8. Beide Änderungen sollen genau einmal eingehen; erwartet wird 54.
- 2Im Test läuft A zuerst vollständig: Es liest 40 und schreibt 46. Danach liest B 46 und schreibt 54. Der Test besteht.
- 3Eine andere mögliche Folge ohne Sperre: Beide lesen 40. A schreibt 46, danach schreibt B seinen berechneten Wert 48.
- 4Der Endwert 48 zeigt den Fehler in dieser anderen Folge. Der bestandene erste Test hat diese Folge nicht ausgeschlossen.
Merke: Ein bestandener Test bestätigt das geprüfte Ergebnis des beobachteten Ablaufs. Er schließt Fehler bei anderen möglichen Reihenfolgen nicht aus.
Teste dich
Alles auf einen Blick
Race Condition
Das Ergebnis nebenläufiger Abläufe hängt von der zeitlichen Reihenfolge ihrer Schritte ab.
Schritte verschiedener Abläufe wechseln sich ab; die Schrittfolge innerhalb jedes Ablaufs bleibt im Modell erhalten.
Ein aus einem alten Wert berechnetes Ergebnis überschreibt die Änderung eines anderen Ablaufs.
Im Rechenmodell schützt dieselbe Sperre beide Abläufe vom Lesen bis einschließlich zum Zurückschreiben.
Ein bestandener Lauf schließt Fehler bei anderen möglichen Reihenfolgen nicht aus.
Den Endwert eines Zählers bestimmen (ausgedachtes Modell)
- 1Der gemeinsame Zähler startet bei 8. A soll um 5 erhöhen, B um 2. Beide Änderungen sollen genau einmal eingehen; erwartet ist der Endwert 15.
- 2A liest 8 und legt diesen Wert als eigenen Zwischenwert ab.
- 3B liest ebenfalls 8 und legt einen eigenen Zwischenwert ab.
- 4A erhöht seinen Zwischenwert auf 13 und schreibt 13 in den gemeinsamen Zähler.
- 5B erhöht seinen zuvor gelesenen Zwischenwert auf 10 und schreibt 10 in den gemeinsamen Zähler.
- 6Der Endwert ist 10. As Erhöhung um 5 fehlt, weil B mit dem alten Wert 8 gerechnet und As Ergebnis überschrieben hat.
- 7Mit derselben Sperre um Lesen, Rechnen und Schreiben könnte A zuerst 13 schreiben. B würde anschließend 13 lesen und 15 schreiben.
Fehler finden
Fehler in Zeile 5Die Schlusszeile berücksichtigt nicht, dass beide mit 100 gerechnet haben. Die Sperre schützt hier nur das Schreiben. Richtig: Nach beiden Schreibschritten steht 120 im gemeinsamen Zähler. As Erhöhung um 10 geht verloren.
Lückentext
Ein erfundener Zähler startet bei 50. A erhöht um 2, B um 6. Beide lesen zuerst 50. Danach schreibt A 52 und zuletzt B 56. Der gemeinsame Endwert ist . Damit sich die gesamten Schrittfolgen aus Lesen, Rechnen und Zurückschreiben nicht überschneiden, erwirbt jeder Ablauf dieselbe Sperre dem Lesen und gibt sie nach dem Zurückschreiben frei. In einem einzelnen Test liest, rechnet und schreibt A fertig, bevor B beginnt. Die Schrittfolge „B liest, bevor A schreibt“ ist in diesem Test .