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.
