7.5.3 Objetos condición

Una variable de condición siempre está asociada con algún tipo de cerrojo; puede proporcionarse explícitamente o se creará uno por omisión. Es útil pasar el cerrojo si varias condiciones van a compartir el mismo cerrojo.

Una variable de condición tiene métodos acquire() y release() que llaman a los métodos correspondientes del cerrojo asociado.También tiene métodos wait(), notify() y notifyAll(). Sólo se debe llamar a éstos cuando el hilo en curso haya adquirido el cerrojo.

El método wait() desbloquea el cerrojo y lo bloquea hasta que sea despertado por una llamada notify() o notifyAll() para la misma condición de otro hilo. Una vez despertada, vuelve a adquirir el cerrojo y retorna. Es posible especificar un plazo máximo de ejecución de la llamada.

El método notify() despierta a uno de los hilos que esperan a la condición, si hay alguno. notifyAll() despierta a todos los hilos que estén esperando la condición.

Observación: Los métodos notify() y notifyAll() no liberan el cerrojo; esto significa que el hilo o hilos despertados no retornarán de su llamada a wait() de inmediato, sino sólo cuando el hilo que llamó a notify() o a notifyAll() libere definitivamente la propiedad del cerrojo.

Truco: La forma típica de utilizar variables de condición utiliza el cerrojo para sincronizar el acceso a algún estado compartido; los hilos interesados en un cambio particular de estado llaman a wait() repetidamente hasta que verifican ese estado, mientras los hilos que modifican el estado llaman a notify() o notifyAll() cuando cambian el estado de modo tal que pudiera suponer un cambio al estado deseado a los que esperan. Por ejemplo, el código a continuación es un modelo productor-consumidor con capacidad de almacenamiento intermedio ilimitado.

# Consumir un elemento
cv.acquire()
while not elemento_disponible():
    cv.wait()
obtener_elemento_disponible()
cv.release()

# Producir un elemento
cv.acquire()
dejar_elemento_disponible()
cv.notify()
cv.release()

Para elegir entre notify() y notifyAll(), hay que considerar si un cambio de estado concreto puede interesar a uno o varios hilos a la espera. Por ejemplo, en una situación típica de productor-consumidor, al añadir un elemento a la memoria intermedia sólo se necesita despertar un hilo consumidor.

Condition ([lock])
Si se proporciona el argumento lock y es diferente de None, debe ser un objeto Lock o RLock que se usará como cerrojo subyacente. En caso contrario, se creará un objeto RLock nuevo para usarlo como cerrojo subyacente.

acquire (*args)
Adquirir el cerrojo subyacente. Este método llama al homónimo del cerrojo; su valor de retorno será el de dicha llamada.

release ()
Liberar el cerrojo subyacente. Este método llama al homónimo del cerrojo; no hay valor de retorno.

wait ([timeout])
Espera una notificación o un vencimiento del plazo de ejecución. Sólo se debe llamar a este método si el hilo en curso ha adquirido el cerrojo.

Este método libera el cerrojo subyacente y bloquea la ejecución del hilo en curso hasta que despierta por una llamada a notify() o notifyAll() para la misma condición en otro hilo o vence el plazo de ejecución opcional timeout. Una vez reanudada la ejecución, readquiere el cerrojo y retorna.

Si está presente el argumento timeout y no es None, debe ser un número en coma flotante que especifique un plazo de ejecución de la operación en segundos.

Si el cerrojo subyacente es un RLock, no se libera usando su método release(), pues esto puede no liberar realmente el cerrojo si se adquirió recursivamente varias veces. En lugar de esto, se utiliza una interfaz interna de la clase RLock que lo libera realmente, aunque hay sido adquirido recursivamente varias veces. Se utiliza otra interfaz interna para restaurar el nivel de recursión al readquirirlo.

notify ()
Despierta a un hilo que espera esta condición, si lo hubiere. Sólo debe llamarse a este método si el hilo en curso ha adquirido el cerrojo.

Este método despierta a uno de lo hilos que esperan la condición, si es que hay alguno. Es una operación nula si no hay hilos a la espera.

La implementación actual despierta exactamente a un hilo, si hay alguno esperando. Sin embargo, no se puede basar el funcionamiento del programa en esto. Una implementación futura optimizada podría despertar más de un hilo.

Nota: el hilo despertado no vuelve realmente de su llamada a wait() hasta que le sea posible readquirir el cerrojo. Como notify() no libera el cerrojo, quien le llame debería hacerlo.

notifyAll ()
Despierta a todos los hilos que esperen esta condición. Este método actúa como notify(), pero despierta a todas las tareas a la espera en lugar de a una sola.


Ver Sobre este documento... para obtener información sobre sugerencias.