Regel
Stellen Sie sicher, dass Thread-Sicherheit Zugriff auf gemeinsam genutzten Zustand.
Gemeinsam genutzte veränderbarer Zustand Zugriff von mehrere Threads
ohne Synchronisation verursacht Wettlauf Bedingungen und Laufzeit .
Unterstützte Sprachen: Python, Java, C#Einleitung
Wenn mehrere Threads ohne Synchronisierung auf gemeinsame Variablen zugreifen und diese modifizieren, treten Race Conditions auf. Der endgültige Wert hängt von unvorhersehbaren Zeitabläufen der Thread-Ausführung ab, was zu Datenkorruption, falschen Berechnungen oder Laufzeitfehlern führt. Ein Zähler, der von mehreren Threads ohne Sperrung inkrementiert wird, verpasst Aktualisierungen, da Threads veraltete Werte lesen, inkrementieren und widersprüchliche Ergebnisse zurückschreiben.
Warum es wichtig ist
Datenkorruption und falsche Ergebnisse: Race Conditions verursachen eine stille Datenkorruption, bei der Werte inkonsistent oder falsch werden. Kontostände können falsch sein, Lagerbestände können negativ sein oder aggregierte Statistiken können korrumpiert werden. Diese Fehler sind schwer zu reproduzieren, da sie von der exakten Thread-Zeitsteuerung abhängen.
Systeminstabilität: Nicht synchronisierter Zugriff auf gemeinsam genutzte Zustände kann Anwendungen zum Absturz bringen. Ein Thread könnte eine Datenstruktur ändern, während ein anderer sie liest, was zu Ausnahmen wie Nullzeigerfehlern oder Index-out-of-bounds-Fehlern führt. Im Produktivbetrieb äußern sich diese als intermittierende Abstürze unter Last.
Debugging-Komplexität: Race Conditions sind notorisch schwer zu debuggen, da sie nicht-deterministisch sind. Der Fehler tritt möglicherweise nicht in Single-Threaded-Tests oder Umgebungen mit geringer Last auf. Die Reproduktion erfordert eine spezifische Thread-Verschränkung, die schwer zu erzwingen ist, wodurch Probleme zufällig erscheinen und verschwinden.
Code-Beispiele
❌ Nicht konform:
Klasse Bankkonto:
def __init__(self):
self.balance = 0
def Einzahlung(self, amount):
current = self.balance
# Race Condition: Ein anderer Thread kann den Kontostand hier ändern
time.sleep(0.001) # Simuliert die Verarbeitungszeit
self.balance = current + amount
def withdraw(self, Betrag):
if self.saldo >= Betrag:
current = self.balance
time.sleep(0,001)
self.balance = current - amount
return True
return False
Warum es falsch ist: Mehrere Threads, die deposit() oder withdraw() gleichzeitig aufrufen, erzeugen Race Conditions. Zwei Threads, die jeweils 100 $ einzahlen, könnten beide den Kontostand als 0 $ lesen und dann beide 100 $ schreiben, was zu einem Endkontostand von 100 $ anstelle von 200 $ führt.
✅ Konform:
import Threading
Klasse BankAccount:
def __init__(self):
self.__balance = 0
self.__lock = threading.Lock()
@property
def balance(self):
mit self.__lock:
return self.__balance
def Einzahlung(self, Betrag):
mit self.__lock:
current = self.__balance
time.sleep(0.001)
self.__balance = current + amount
def withdraw(self, Betrag):
mit self.__lock:
wenn self.__balance >= Betrag:
current = self.__balance
time.sleep(0.001)
self.__balance = current - amount
return True
return False
Warum dies wichtig ist: Der threading.Lock() stellt sicher, dass nur ein Thread gleichzeitig auf den Saldo zugreift. Wenn ein Thread die Sperre hält, warten andere, wodurch gleichzeitige Änderungen verhindert werden. Privat __balance mit readonly @property verhindert, dass externer Code den Sperrschutz umgeht.
Fazit
Schützen Sie alle gemeinsam genutzten, veränderlichen Zustände mit geeigneten Synchronisationsprimitiven wie Locks, Semaphoren oder atomaren Operationen. Bevorzugen Sie, wenn möglich, unveränderliche Datenstrukturen oder Thread-Local Storage. Wenn Synchronisation erforderlich ist, minimieren Sie kritische Abschnitte, um Konflikte zu reduzieren und die Leistung zu verbessern.

