Bloqueo de hilo en Python
- Condición de carrera en Python
- Bloqueo de hilo en Python
-
Bloqueo de hilo usando
with lock:
en Python
Este tutorial discutirá diferentes métodos para utilizar un bloqueo de hilo en Python.
Condición de carrera en Python
Una condición de carrera es un problema que ocurre cuando varios subprocesos intentan modificar la misma variable compartida. Todos los hilos leen el mismo valor de la variable compartida al mismo tiempo. Luego, todos los hilos intentan modificar el valor de la variable compartida. Pero, la variable solo termina almacenando el valor del último hilo porque sobrescribe el valor escrito por el hilo anterior. En este sentido, hay una carrera entre todos los hilos para ver cuál modifica al final el valor de la variable. Este fenómeno se demuestra con un ejemplo en el siguiente código.
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}")
Producción :
counter=10
counter=20
The final counter is 20
Tenemos una variable compartida global contador = 0
y dos subprocesos t1
y t2
. El hilo t1
intenta incrementar el valor de counter
en 10 y el hilo t2
intenta incrementar el valor de counter
en 20. En el código anterior, ejecutamos ambos hilos simultáneamente e intentamos modificar el valor de counter
. Según la lógica anterior, el valor final de counter
debe tener el valor 30. Pero, debido a la condición de carrera, el counter
es 10 o 20.
Bloqueo de hilo en Python
El bloqueo de rosca se utiliza para evitar la condición de carrera. El bloqueo de subprocesos bloquea el acceso a una variable compartida cuando es utilizado por un subproceso para que ningún otro subproceso no pueda acceder a él y luego elimina el bloqueo cuando el subproceso no está utilizando la variable compartida para que la variable esté disponible para otros subprocesos para su procesamiento. La clase Lock
dentro del módulo de subprocesos se utiliza para crear un bloqueo de subprocesos en Python. El método acquire()
se usa para bloquear el acceso a una variable compartida, y el método release()
se usa para desbloquear el bloqueo. El método release()
arroja una excepción RuntimeError
si se usa en un bloqueo desbloqueado.
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}")
Producción :
counter=10
counter=30
The final counter is 30
Creamos una variable compartida globalmente compartida counter=0
y dos subprocesos t1
y t2
. Ambos hilos tienen como objetivo la misma función increase()
. La función increase(by, lock)
toma dos parámetros. El primer parámetro es la cantidad en la que se incrementará el counter
, y el segundo parámetro es una instancia de la clase Lock
. Además de las declaraciones anteriores, también creamos una instancia lock
de la clase Lock
dentro del módulo threading
de Python. Este parámetro block
en la función increase(by, lock)
bloquea el acceso a la variable counter
con la función lock.acquire()
mientras es modificada por cualquier hilo y desbloquea el bloqueo con la función lock.release()
cuando un hilo ha modificado la variable counter
. El hilo t1
incrementa el valor de counter
en 10, y el hilo t2
incrementa el valor de counter
en 20.
Debido al bloqueo del hilo, la condición de carrera no ocurre y el valor final del counter
es 30.
Bloqueo de hilo usando with lock:
en Python
El problema con el método anterior es que tenemos que desbloquear cuidadosamente cada variable bloqueada cuando un hilo ha completado el procesamiento. Si no se hace correctamente, nuestra variable compartida solo será accedida por el primer hilo, y ningún otro hilo tendrá acceso a la variable compartida. Este problema se puede evitar utilizando la gestión de contexto. Podemos usar with lock:
y colocar todo nuestro código crítico dentro de este bloque. Esta es una forma mucho más fácil de prevenir condiciones de carrera. El siguiente fragmento de código muestra el uso de with lock:
para prevenir la condición de carrera 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}")
Producción :
counter=10
counter=30
The final counter is 30
Colocamos nuestro código para incrementar counter
dentro del bloque with lock:
. El hilo t1
incrementa el valor de counter
en 10, y el hilo t2
incrementa el valor de counter
en 20. La condición de carrera no ocurre, y el valor final de counter
es 30. Además , No tenemos que preocuparnos por desbloquear el bloqueo del hilo.
Ambos métodos hacen su trabajo a la perfección, es decir, ambos métodos evitan que ocurra la condición de carrera, pero el segundo método es muy superior al primero porque nos evita el dolor de cabeza de lidiar con el bloqueo y desbloqueo de bloqueos de hilo. También es mucho más limpio de escribir y más fácil de leer que el primer método.
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