Exemples pratiques et solutions
Pour mieux illustrer les concepts de conditions de course et les techniques pour les prévenir, explorons quelques exemples pratiques et solutions.
Exemple 1 : Incrémentation d'un compteur
Supposons qu'il existe un compteur partagé que plusieurs threads doivent incrémenter. Sans une synchronisation appropriée, une condition de course peut se produire, entraînant un compte final incorrect.
import threading
## Compteur partagé
compteur = 0
def incrementer_compteur():
global compteur
for _ in range(1000000):
compteur += 1
## Créez et lancez les threads
threads = [threading.Thread(target=incrementer_compteur) for _ in range(4)]
for thread in threads:
thread.start()
## Attendez que tous les threads aient fini
for thread in threads:
thread.join()
print(f"Valeur finale du compteur : {compteur}")
Pour prévenir la condition de course, nous pouvons utiliser un Mutex pour nous assurer qu'un seul thread peut accéder au compteur partagé à la fois :
import threading
## Compteur partagé
compteur = 0
verrou = threading.Lock()
def incrementer_compteur():
global compteur
for _ in range(1000000):
with verrou:
compteur += 1
## Créez et lancez les threads
threads = [threading.Thread(target=incrementer_compteur) for _ in range(4)]
for thread in threads:
thread.start()
## Attendez que tous les threads aient fini
for thread in threads:
thread.join()
print(f"Valeur finale du compteur : {compteur}")
Exemple 2 : Compte bancaire partagé
Considérez un scénario où plusieurs threads accèdent à un compte bancaire partagé. Sans une synchronisation appropriée, une condition de course peut se produire, entraînant des soldes de compte incorrects.
import threading
## Compte bancaire partagé
solde = 1000
def retrait(montant):
global solde
if solde >= montant:
solde -= montant
print(f"Retrait de {montant}, nouveau solde : {solde}")
else:
print("Fonds insuffisants")
def depot(montant):
global solde
solde += montant
print(f"Déposé {montant}, nouveau solde : {solde}")
## Créez et lancez les threads
thread_retrait = threading.Thread(target=retrait, args=(500,))
thread_depot = threading.Thread(target=depot, args=(200,))
thread_retrait.start()
thread_depot.start()
## Attendez que tous les threads aient fini
thread_retrait.join()
thread_depot.join()
Pour prévenir la condition de course, nous pouvons utiliser un Mutex pour nous assurer qu'un seul thread peut accéder au compte bancaire partagé à la fois :
import threading
## Compte bancaire partagé
solde = 1000
verrou = threading.Lock()
def retrait(montant):
global solde
with verrou:
if solde >= montant:
solde -= montant
print(f"Retrait de {montant}, nouveau solde : {solde}")
else:
print("Fonds insuffisants")
def depot(montant):
global solde
with verrou:
solde += montant
print(f"Déposé {montant}, nouveau solde : {solde}")
## Créez et lancez les threads
thread_retrait = threading.Thread(target=retrait, args=(500,))
thread_depot = threading.Thread(target=depot, args=(200,))
thread_retrait.start()
thread_depot.start()
## Attendez que tous les threads aient fini
thread_retrait.join()
thread_depot.join()
Ces exemples démontrent comment les conditions de course peuvent se produire en multithreading Python et comment utiliser des techniques de synchronisation, telles que les Mutex, pour les prévenir. En comprenant et en appliquant ces concepts, vous pouvez écrire des applications concurrentes plus fiables et robustes en Python.