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 }
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.
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
.