RLock()
(reentrant lock) en Python es similar a Lock()
, pero con una diferencia importante: permite que el mismo hilo que ha adquirido el bloqueo (lock
) lo vuelva a adquirir sin bloquearse a sí mismo. En términos simples, RLock
permite que el mismo hilo pueda «reentrar» en una sección de código bloqueada.
RLock()
?RLock()
es útil cuando se trabaja con estructuras de código en las que una función llama a otra función, y ambas requieren acceso exclusivo al mismo recurso compartido. En estos casos, si usáramos Lock
, el hilo se bloquearía cuando intentara adquirir el lock
por segunda vez. RLock()
permite que el mismo hilo adquiera el bloqueo varias veces, siempre que lo libere el mismo número de veces.
RLock()
A continuación, un ejemplo donde una función llama a otra función, ambas modifican una variable compartida y ambas requieren un lock
:
import threading
# Variable compartida
contador = 0
# Creamos un objeto RLock
rlock = threading.RLock()
# Función para incrementar
def incrementar():
global contador
with rlock:
contador += 1
# Llama a otra función que también usa el mismo RLock
incrementar_extra()
# Función que realiza un incremento adicional
def incrementar_extra():
global contador
with rlock:
contador += 1
# Creamos múltiples hilos que ejecutan la función incrementar
hilos = []
for _ in range(10): # Crearemos 10 hilos
hilo = threading.Thread(target=incrementar)
hilos.append(hilo)
hilo.start()
# Esperamos a que todos los hilos terminen
for hilo in hilos:
hilo.join()
print("Valor final del contador:", contador)
contador
es la variable global que será modificada por varios hilos.rlock
utilizando threading.RLock()
para manejar la sincronización en las funciones incrementar
e incrementar_extra
.incrementar
:
rlock
utilizando el contexto with rlock
, lo que asegura que contador
se modifique de forma segura.incrementar_extra
, que también intenta adquirir el rlock
.incrementar_extra
:
rlock
para incrementar nuevamente el contador. Dado que es el mismo hilo el que ya tiene el rlock
, puede adquirirlo sin problema.incrementar
.hilo.join()
, esperamos que todos los hilos terminen su ejecución antes de imprimir el valor final de contador
.RLock
Si usáramos un Lock
en lugar de RLock
, el programa se bloquearía al intentar que incrementar_extra
adquiriera el lock
por segunda vez en el mismo hilo. Con RLock
, el mismo hilo puede «reentrar» en la sección crítica, evitando el bloqueo y permitiendo una correcta sincronización entre hilos.
Rlock() permite un uso recursivo. Su funcionamiento es el siguiente:
import threading
algo = 0
rlock = threading.RLock()
rlock.acquire()
algo += 1
rlock.acquire()
algo += 2
rlock.release()
rlock.release()
print(algo)
Un lock reentrante es una primitiva de sincronización que puede ser adquirido múltiples veces por el mismo hilo. Internamente, utiliza el concepto de «hilo dueño» y «nivel de recursividad» además del estado abierto/cerrado utilizado por las primitivas locks. Si está en estado cerrado, algún hilo es dueño del lock; si está en estado abierto, ningún hilo es dueño.
Para cerrar el lock, un hilo llama a su método acquire(); esto retorna una vez que el hilo se ha adueñado del lock. Para abrir el lock, un hilo llama a su método release(). Pares de llamadas acquire()/release() pueden anidarse; sólo el release() final (el release() del par más externo) restablece el lock a abierto y permite que otro hilo bloqueado en acquire() proceda.
Los locks reentrantes también soportan el protocolo de manejo de contextos.
sta clase implementa objetos tipo lock reentrantes. Un lock reentrante debe ser liberado por el hilo que lo adquirió. Una vez que un hilo ha adquirido un lock reentrante, el mismo hilo puede adquirirlo otra vez sin bloquearse; el hilo debe liberarlo una vez por vez que lo adquiere.
Nótese que RLock en realidad es una función fábrica que retorna una instancia de la versión más eficiente de la clase RLock concreta que sea soportada por la plataforma.
acquire(blocking=True, timeout=-1)
Adquirir un lock, bloqueante o no bloqueante.
Cuando se invoca sin argumentos: si este hilo ya es dueño del lock, incrementa el nivel de recursividad en uno, y retorna inmediatamente. De otro modo, si otro hilo es dueño del lock, bloquea hasta que se abra el lock. Una vez que el lock se abra (ningún hilo sea su dueño), se adueña, establece el nivel de recursividad en uno, y retorna. Si más de un hilo está bloqueado esperando que sea abra el lock, solo uno a la vez podrá apoderarse del lock. No hay valor de retorno en este caso.
When invoked with the blocking argument set to True, do the same thing as when called without arguments, and return True.
When invoked with the blocking argument set to False, do not block. If a call without an argument would block, return False immediately; otherwise, do the same thing as when called without arguments, and return True.
When invoked with the floating-point timeout argument set to a positive value, block for at most the number of seconds specified by timeout and as long as the lock cannot be acquired. Return True if the lock has been acquired, False if the timeout has elapsed.
Distinto en la versión 3.2: El parámetro timeout es nuevo.
Libera un lock, disminuyendo el nivel de recursividad. Si después de la disminución es cero, restablece el lock a abierto (no perteneciente a ningún hilo), y si cualquier otro hilo está bloqueado esperando que se abra el lock, permite que exactamente uno de ellos proceda. Si luego de la disminución el nivel de recursividad todavía no es cero, el lock permanece cerrado y perteneciente al hilo llamador.
Solo llámese este método cuando el hilo llamador sea dueño del lock. Se lanza un RuntimeError si se llama este método cuando el lock esta abierto.
No hay valor de retorno.
Problema: Imagina que estás implementando una función recursiva para calcular el factorial de un número. Sin embargo, esta función puede ser llamada simultáneamente por varios hilos. Tu tarea es asegurarte de que, cuando un hilo está calculando el factorial, ningún otro hilo puede interrumpir su cálculo. Para ello, puedes utilizar la instrucción RLock.
import threading
class Factorial:
def __init__(self):
self.lock = threading.RLock()
def calcular_factorial(self, n):
with self.lock:
if n == 0:
return 1
else:
return n * self.calcular_factorial(n-1)
def hilo(factorial, n):
resultado = factorial.calcular_factorial(n)
print(f"El factorial de {n} es {resultado}.")
factorial = Factorial()
hilos = []
for i in range(1, 6):
t = threading.Thread(target=hilo, args=(factorial, i))
hilos.append(t)
t.start()
for t in hilos:
t.join()
En este código, cada hilo representa a un hilo que intenta calcular el factorial de un número. El método calcular_factorial utiliza un RLock para asegurarse de que solo un hilo puede acceder a la función a la vez, incluso de manera recursiva. Al final, se imprime el resultado del cálculo del factorial.
En Python, self es el primer parámetro que reciben los métodos de un objeto cuando lo invocas con la sintaxis: objeto.metodo(). El método recibirá como primer parámetro una referencia al objeto desde el que fue invocado y a través de él podrás acceder a atributos de ese objeto1.
Aunque self no es una palabra clave en Python, es una convención ampliamente aceptada utilizar el nombre self para referirse a este parámetro2. Al definir una clase, los métodos que se crean dentro de ella deben tener como primer parámetro self, aunque este nombre puede ser reemplazado por cualquier otro identificador válido en Python3.
Para utilizar self, debes declarar este parámetro como el primer argumento en la definición del método. Es importante destacar que self solo se utiliza dentro de la clase y no se pasa como argumento cuando se llama al método2.
En resumen, self en Python es una forma de acceder a los atributos y métodos de la instancia específica de una clase. Esto es fundamental para la programación orientada a objetos en Python.
Sí, self en Python es equivalente a this en Java en términos de su uso para referirse a la instancia actual de una clase. Sin embargo, hay algunas diferencias clave12:
En Python, self no es una palabra clave, simplemente es una convención. El primer parámetro de un método en Python se refiere a la instancia de la clase, y por convención, se le llama self34. Pero puedes usar cualquier nombre válido en Python para este parámetro3.
En Java, this es una palabra clave y se refiere automáticamente a la instancia actual de una clase2.
En Python, debes incluir self explícitamente como primer parámetro en la definición de un método12. En Java, this es implícito y no necesitas declararlo1.
En Python, self se utiliza para acceder a los atributos y métodos de la instancia específica de una clase4. En Java, this se utiliza de manera similar para referirse a la instancia actual de una clase2.
Por lo tanto, aunque self en Python y this en Java se utilizan de manera similar, la forma en que se implementan y utilizan en los dos lenguajes es diferente.