Señales y Esperas Condicionales

El módulo Thread::Queue proporciona listas que pueden ser usadas concurrentemente sin peligro (thread-safe). Cualquier thread puede añadir o suprimir elementos de la lista. Sin embargo no es posible insertar en medio de la lista.

Una cola compartida viene definida por su constructor:

67  sub new {
68      my $class = shift;
69      my @q : shared = @_;
70      return bless \@q, $class;
71  }

Señales Condicionales

Una señal condicional permite a una thread desbloquear a una thread que espera por una variable condicional. Un broadcast condicional es similar pero libera a todas las threads que esperan en esa variable.

Por ejemplo, en el módulo Thread::Queue vemos el siguiente código:

87  sub enqueue {
88      my $q = shift;
89      lock(@$q);
90      push @$q, @_  and cond_signal @$q;
91  }
La llamada a cond_signal desbloquea una thread que esta bloqueada esperando por @$q. La función cond_signal toma como argumento una variable bloquedada (locked) como parámetro y desbloquea una thread que esta en espera condicional por dicha variable. Si no hay threads esperando la señal es descartada.

La subrutina enqueue obtiene el cerrojo sobre la cola (@$q). Si la operación de push tiene éxito retorna el número de elementos en el array después del push. Por tanto si hay elementos en la cola se ejecuta cond_signal @$q. Esta llamada desbloquea una thread que este esperando condicionalmente con cond_waiting en la variable @$q. Si hay mas de una, sólo una será elegida. La elección es no determinista.

Espera en una Condición

El anverso de cond_signal es cond_wait . La espera en una condición permite a la thread esperar por una condición y liberar atómicamente el mutex que se usa para garantizar la condición. La thread espera que otra thread haga verdadera la condición. La correspondiente llamada de esa thread a cond_signal produce la reactivación de la thread en espera.

La llamada a cond_wait libera el mutex asociado con la variable atómicamente y hace que la thread se bloquee hasta que se cumpla la condición. La thread bloqueada puede ser despertada mediante cond_signal o cond_broadcast .

Es importante tener en cuenta que la variable puede recibir una notificación sin que se haya producido una cond_signal o cond_broadcast. Por ello es necesario comprobar el valor de la variable y volver a esperar si los requisitos no se cumplen. Es por eso que dequeue contiene el condicional until @$q en la línea 76:

73  sub dequeue  {
74      my $q = shift;
75      lock(@$q);
76      cond_wait @$q until @$q;     # Aunque tengamos el cerrojo
77      cond_signal @$q if @$q > 1;  # Debemos esperar a que haya algo
78      return shift @$q;
79  }

En la subrutina dequeue después de obtener el cerrojo sobre la cola @$q se procede a llamar a cond_wait si la cola esta vacía. Si la cola no esta vacía se salta la llamada y se desbloqueará una thread que este esperando condicionalmente si hay dos o más elementos en la cola (línea cond_signal @$q if @$q > 1). Si la cola esta vacía, la llamada a cond_wait suelta el cerrojo sobre la variable @$q y bloquea el proceso hasta que otra thread ejecuta un cond_signal sobre la variable @$q.



Subsecciones
Casiano Rodríguez León
2012-02-29