Wenn zwei Abläufe sich in die Quere kommen
Eine Race Condition entsteht, wenn zwei Threads dieselben Daten ändern und das Ergebnis von der zufälligen Reihenfolge abhängt. Ein Deadlock ist der Stillstand, bei dem jeder auf etwas wartet, das ein anderer festhält.
Beide Fehler fehlen oft im Test und treten erst im Betrieb auf. Stellen Sie sich zwei Kassen vor, die den letzten Joghurt im Regal gleichzeitig verkaufen.
Am Ende dieser Lektion
Notiz
Ich kann erklären, wie eine Race Condition und ein Deadlock entstehen und woran man sie erkennt.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Thread | ein Ablauf innerhalb eines Prozesses, der Speicher mit anderen teilt | |
| Race Condition | ein Fehler, bei dem die Reihenfolge das Ergebnis ändert | |
| Kritischer Abschnitt | ein Programmteil, den nur ein Thread zugleich betreten darf | |
| Sperre | ein Schloss, das nur ein Thread hält | Lock |
| Deadlock | ein Stillstand, weil jeder auf einen anderen wartet | |
| Wartegraph | ein Graph, der zeigt, wer auf wen wartet |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Threads teilen sich den Speicher eines Prozesses
Ein Prozess kann mehrere Threads haben. Jeder Thread läuft für sich und kann auf einem eigenen Kern rechnen. Alle Threads eines Prozesses teilen sich aber denselben Speicher. Das ist praktisch, denn sie tauschen Daten ohne Umweg aus.
Es ist auch gefährlich. Zwei Threads können gleichzeitig dieselbe Variable ändern, und niemand bestimmt, wer zuerst dran ist. Linux zeigt die Zahl der Threads in der Datei status.
Wie viele Threads hat grep?
grep Threads /proc/self/statusHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Warum zwei Threads einen Zähler falsch zählen lassen
Der Befehl zaehler = zaehler + 1 sieht einfach aus, besteht aber aus drei Schritten: lesen, addieren, schreiben. Unterbricht das System einen Thread zwischen Lesen und Schreiben, überschreibt der andere später dessen Ergebnis.
Das ist eine Race Condition. Das Ergebnis hängt davon ab, wer schneller war. Die Tabelle zeigt, wie ein Schritt verloren geht.
Beispiel: zwei Threads addieren je 1
| Schritt | Thread A | Thread B | Zähler im Speicher |
|---|---|---|---|
| 1 | liest 5 | 5 | |
| 2 | liest 5 | 5 | |
| 3 | schreibt 6 | 6 | |
| 4 | schreibt 6 | 6 |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein häufiger Fehler
Warnung
Viele glauben, eine Race Condition finde man durch Ausprobieren. Sie tritt aber nur bei ungünstiger Reihenfolge auf und fehlt oft in hundert Läufen. Eine Sperre um den kritischen Abschnitt verhindert sie sicher. Verlassen Sie sich nicht auf Glück beim Testen.
Eine Sperre lässt nur einen Thread hinein
Den Teil, der gemeinsame Daten ändert, nennt man kritischen Abschnitt. Eine Sperre schützt ihn. Ein Thread nimmt die Sperre, arbeitet und gibt sie wieder frei. Wer die Sperre nicht bekommt, wartet.
Das Programm flock zeigt das mit einer Sperrdatei. Der Befehl unten nimmt die Sperre und versucht dann, sie ein zweites Mal zu nehmen. Die Option -n heißt: nicht warten, sondern sofort aufgeben. Der zweite Versuch scheitert, und es erscheint das Wort gesperrt.
Die zweite Sperre scheitert
flock -n /tmp/osint.lock sh -c "flock -n /tmp/osint.lock echo frei || echo gesperrt"Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ein Deadlock entsteht durch gegenseitiges Warten
Mit Sperren droht ein neuer Fehler. Thread A hält Sperre 1 und will Sperre 2. Thread B hält Sperre 2 und will Sperre 1. Beide warten für immer. Das ist ein Deadlock.
Dafür müssen vier Bedingungen zugleich gelten. Die Sperre gehört nur einem. Ein Thread hält eine und wartet auf eine weitere. Niemand kann sie ihm wegnehmen. Die Wartenden bilden einen Kreis.
Im Wartegraph zeigt ein Kreis einen Deadlock
Der Wartegraph hat einen Knoten je Prozess. Ein Pfeil von A nach B heißt: A wartet auf etwas, das B hält. Führt eine Folge von Pfeilen zum Ausgangspunkt zurück, ist das ein Kreis, und alle Prozesse auf dem Kreis stecken fest.
Gibt es keinen Kreis, kommt irgendwann jeder frei. Ein Prozess, der nur auf einen Prozess im Kreis wartet, liegt nicht selbst auf dem Kreis. Er kommt aber auch nicht frei.
Eine feste Reihenfolge der Sperren verhindert Deadlocks
Der einfachste Schutz ist eine feste Reihenfolge: Alle Threads nehmen Sperre 1 vor Sperre 2. Dann kann kein Kreis entstehen, denn niemand hält Sperre 2 und will Sperre 1.
Andere Wege sind eine Wartezeit mit Abbruch oder alle Sperren auf einmal anzufordern. Manche Systeme erkennen den Kreis und beenden einen der Prozesse.
Noch ein häufiger Fehler
Warnung
Viele halten einen Deadlock für ein langsames Programm. Ein langsames Programm kommt irgendwann an. Ein Deadlock endet nie von selbst, denn jeder wartet auf einen, der ebenfalls wartet. Hilfe bringt nur, einen Prozess abzubrechen.