Verrouillage de thread en Python
- Condition de course en Python
- Verrouillage de thread en Python
-
Thread Lock Utilisation de
with lock:
en Python
Ce didacticiel abordera différentes méthodes pour utiliser un verrou de thread en Python.
Condition de course en Python
Une condition de concurrence critique est un problème qui se produit lorsque plusieurs threads tentent de modifier la même variable partagée. Tous les threads lisent la même valeur à partir de la variable partagée en même temps. Ensuite, tous les threads essaient de modifier la valeur de la variable partagée. Mais, la variable ne finit par stocker que la valeur du dernier thread car elle écrase la valeur écrite par le thread précédent. En ce sens, il y a une course entre tous les threads pour voir lequel modifie la valeur de la variable à la fin. Ce phénomène est illustré par un exemple dans le code suivant.
from threading import Thread
counter = 0
def increase(by):
global counter
local_counter = counter
local_counter += by
counter = local_counter
print(f"counter={counter}")
t1 = Thread(target=increase, args=(10,))
t2 = Thread(target=increase, args=(20,))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"The final counter is {counter}")
Production :
counter=10
counter=20
The final counter is 20
Nous avons une variable globale partagée counter = 0
et deux threads t1
et t2
. Le thread t1
essaie d’incrémenter la valeur de counter
de 10 et le thread t2
essaie d’incrémenter la valeur de counter
de 20. Dans le code ci-dessus, nous exécutons les deux threads simultanément et essayons de modifier la valeur de counter
. Selon la logique ci-dessus, la valeur finale de counter
devrait avoir la valeur 30. Mais, en raison de la condition de concurrence, le counter
est soit 10, soit 20.
Verrouillage de thread en Python
Le verrou de filetage est utilisé pour empêcher la condition de concurrence. Le verrou de thread verrouille l’accès à une variable partagée lorsqu’elle est utilisée par un thread afin qu’aucun autre thread ne puisse y accéder, puis supprime le verrou lorsque le thread n’utilise pas la variable partagée afin que la variable soit disponible pour d’autres threads pour le traitement. La classe Lock
à l’intérieur du module de threading est utilisée pour créer un verrou de thread en Python. La méthode acquire()
est utilisée pour verrouiller l’accès à une variable partagée, et la méthode release()
est utilisée pour déverrouiller le verrou. La méthode release()
lève une exception RuntimeError
si elle est utilisée sur un verrou déverrouillé.
from threading import Thread, Lock
counter = 0
def increase(by, lock):
global counter
lock.acquire()
local_counter = counter
local_counter += by
counter = local_counter
print(f"counter={counter}")
lock.release()
lock = Lock()
t1 = Thread(target=increase, args=(10, lock))
t2 = Thread(target=increase, args=(20, lock))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"The final counter is {counter}")
Production :
counter=10
counter=30
The final counter is 30
Nous avons créé une variable globalement partagée counter=0
et deux threads t1
et t2
. Les deux threads ciblent la même fonction increase()
. La fonction increase(by, lock)
prend deux paramètres. Le premier paramètre est la quantité par laquelle il incrémentera counter
, et le deuxième paramètre est une instance de la classe Lock
. En plus des déclarations précédentes, nous avons également créé une instance Lock
de la classe Lock
à l’intérieur du module threading
de Python. Ce paramètre lock
de la fonction increase(by, lock)
verrouille l’accès à la variable counter
avec la fonction lock.acquire()
tant qu’elle est modifiée par n’importe quel thread et déverrouille le verrou avec la fonction lock.release()
lorsqu’un thread a modifié la variable counter
. Le thread t1
incrémente la valeur de counter
de 10, et le thread t2
incrémente la valeur de counter
de 20.
En raison du verrou de thread, la condition de concurrence ne se produit pas et la valeur finale de counter
est 30.
Thread Lock Utilisation de with lock:
en Python
Le problème avec la méthode précédente est que nous devons soigneusement déverrouiller chaque variable verrouillée lorsqu’un thread a terminé le traitement. Si ce n’est pas fait correctement, notre variable partagée ne sera accessible que par le premier thread, et aucun autre thread n’aura accès à la variable partagée. Ce problème peut être évité en utilisant la gestion de contexte. Nous pouvons utiliser avec verrou :
et placer tout notre code critique à l’intérieur de ce bloc. C’est un moyen beaucoup plus facile d’éviter les conditions de course. L’extrait de code suivant montre l’utilisation de with lock:
pour empêcher la condition de concurrence en Python.
from threading import Thread, Lock
counter = 0
def increase(by, lock):
global counter
with lock:
local_counter = counter
local_counter += by
counter = local_counter
print(f"counter={counter}")
lock = Lock()
t1 = Thread(target=increase, args=(10, lock))
t2 = Thread(target=increase, args=(20, lock))
t1.start()
t2.start()
t1.join()
t2.join()
print(f"The final counter is {counter}")
Production :
counter=10
counter=30
The final counter is 30
Nous avons placé notre code pour incrémenter counter
à l’intérieur du bloc with lock:
. Le thread t1
incrémente la valeur de counter
de 10, et le thread t2
incrémente la valeur de counter
de 20. La condition de concurrence ne se produit pas, et la valeur finale de counter
est 30. De plus , Nous n’avons pas à nous soucier du déverrouillage du verrou de fil.
Les deux méthodes font parfaitement leur travail, c’est-à-dire que les deux méthodes empêchent la condition de concurrence de se produire, mais la deuxième méthode est de loin supérieure à la première car elle nous évite les maux de tête liés au verrouillage et au déverrouillage des verrous de thread. Il est également beaucoup plus propre à écrire et plus facile à lire que la première méthode.
Maisam is a highly skilled and motivated Data Scientist. He has over 4 years of experience with Python programming language. He loves solving complex problems and sharing his results on the internet.
LinkedIn