Praktische Beispiele und Lösungen
Um die Konzepte von Wettlaufbedingungen und die Techniken zur Verhinderung dieser Bedingungen besser zu veranschaulichen, betrachten wir einige praktische Beispiele und Lösungen.
Beispiel 1: Zählerinkrement
Angenommen, wir haben einen gemeinsam genutzten Zähler, den mehrere Threads inkrementieren müssen. Ohne eine geeignete Synchronisation kann eine Wettlaufbedingung auftreten, was zu einem falschen Endwert führt.
import threading
## Gemeinsamer Zähler
counter = 0
def increment_counter():
global counter
for _ in range(1000000):
counter += 1
## Erstellen und Starten der Threads
threads = [threading.Thread(target=increment_counter) for _ in range(4)]
for thread in threads:
thread.start()
## Warten, bis alle Threads fertig sind
for thread in threads:
thread.join()
print(f"Endwert des Zählers: {counter}")
Um die Wettlaufbedingung zu vermeiden, können wir einen Mutex verwenden, um sicherzustellen, dass zu einem bestimmten Zeitpunkt nur ein Thread auf den gemeinsam genutzten Zähler zugreifen kann:
import threading
## Gemeinsamer Zähler
counter = 0
lock = threading.Lock()
def increment_counter():
global counter
for _ in range(1000000):
with lock:
counter += 1
## Erstellen und Starten der Threads
threads = [threading.Thread(target=increment_counter) for _ in range(4)]
for thread in threads:
thread.start()
## Warten, bis alle Threads fertig sind
for thread in threads:
thread.join()
print(f"Endwert des Zählers: {counter}")
Beispiel 2: Gemeinsames Bankkonto
Betrachten Sie einen Szenario, in dem mehrere Threads auf ein gemeinsames Bankkonto zugreifen. Ohne eine geeignete Synchronisation kann eine Wettlaufbedingung auftreten, was zu falschen Kontoständen führt.
import threading
## Gemeinsames Bankkonto
balance = 1000
def withdraw(amount):
global balance
if balance >= amount:
balance -= amount
print(f"Abgebucht: {amount}, neuer Kontostand: {balance}")
else:
print("Nicht genug Guthaben")
def deposit(amount):
global balance
balance += amount
print(f"Eingezahlt: {amount}, neuer Kontostand: {balance}")
## Erstellen und Starten der Threads
withdraw_thread = threading.Thread(target=withdraw, args=(500,))
deposit_thread = threading.Thread(target=deposit, args=(200,))
withdraw_thread.start()
deposit_thread.start()
## Warten, bis alle Threads fertig sind
withdraw_thread.join()
deposit_thread.join()
Um die Wettlaufbedingung zu vermeiden, können wir einen Mutex verwenden, um sicherzustellen, dass zu einem bestimmten Zeitpunkt nur ein Thread auf das gemeinsame Bankkonto zugreifen kann:
import threading
## Gemeinsames Bankkonto
balance = 1000
lock = threading.Lock()
def withdraw(amount):
global balance
with lock:
if balance >= amount:
balance -= amount
print(f"Abgebucht: {amount}, neuer Kontostand: {balance}")
else:
print("Nicht genug Guthaben")
def deposit(amount):
global balance
with lock:
balance += amount
print(f"Eingezahlt: {amount}, neuer Kontostand: {balance}")
## Erstellen und Starten der Threads
withdraw_thread = threading.Thread(target=withdraw, args=(500,))
deposit_thread = threading.Thread(target=deposit, args=(200,))
withdraw_thread.start()
deposit_thread.start()
## Warten, bis alle Threads fertig sind
withdraw_thread.join()
deposit_thread.join()
Diese Beispiele zeigen, wie Wettlaufbedingungen in Python-Multithreading auftreten können und wie Synchronisationstechniken wie Mutexe verwendet werden können, um sie zu vermeiden. Indem Sie diese Konzepte verstehen und anwenden, können Sie in Python zuverlässigere und robusterere parallele Anwendungen schreiben.