Pthread y condiciones de espera


Estoy aprendiendo pthread y las condiciones de espera. Por lo que puedo decir, un hilo de espera típico es como este:

pthread_mutex_lock(&m);
while(!condition)
     pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

Lo que no puedo entender es por qué la línea while(!condition) es necesaria incluso si utilizo pthread_cond_signal() para despertar el hilo.

Puedo entender que si utilizo pthread_cond_broadcast() necesito probar la condición, porque despierto todos los subprocesos en espera y uno de ellos puede hacer que la condición sea falsa nuevamente antes de desbloquear el mutex (y así transferir la ejecución a otro subproceso despertado que no debe ejecutar en ese punto). Pero si uso pthread_cond_signal()me despierto solo un hilo por lo que la condición debe ser verdadera. Así que el código podría verse así:

pthread_mutex_lock(&m);
pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

Leí algo sobre señales espurias que pueden suceder. ¿Es esta (y solo esta) la razón? ¿Por qué debería tener singnals falsos? ¿O hay algo más que no entiendo?

Asumo que el código de la señal es así:

pthread_mutex_lock(&m);
condition = true;
pthread_cond_signal(&cond); // Should wake up *one* thread
pthread_mutex_unlock(&m);
Author: Emiliano, 2009-07-16

2 answers

La verdadera razón por la que deberías poner pthread_cond_wait en un bucle while no es debido a un despertar espurio. Incluso si su variable de condición no tuviera un despertar espurio, aún necesitaría el bucle para detectar un tipo común de error. ¿Por qué? Considere lo que puede suceder si varios hilos esperan en la misma condición:

Thread 1                         Thread 2           Thread 3
check condition (fails)
(in cond_wait) unlock mutex
(in cond_wait) wait
                                 lock mutex
                                 set condition
                                 signal condvar
                                 unlock mutex
                                                    lock mutex
                                                    check condition (succeeds)
                                                    do stuff
                                                    unset condition
                                                    unlock mutex
(in cond_wait) wake up
(in cond_wait) lock mutex
<thread is awake, but condition
is unset>

El problema aquí es que el subproceso debe liberar el mutex antes de esperar, lo que potencialmente permite que otro subproceso 'robe' lo que ese subproceso estaba esperando. A menos que se garantiza que solo un hilo puede esperar en esa condición, es incorrecto asumir que la condición es válida cuando un hilo se despierta.

 43
Author: Chris Lu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-27 20:47:37

Supongamos que no comprueba la condición. Entonces, por lo general, no puede evitar que suceda lo siguiente malo (al menos, no puede evitarlo en una línea de código):

 Sender                             Receiver
locks mutex
sets condition
signals condvar, but nothing 
  is waiting so has no effect
releases mutex
                                    locks mutex
                                    waits. Forever.

Por supuesto, su segundo ejemplo de código podría evitar esto haciendo:

pthread_mutex_lock(&m);
if (!condition) pthread_cond_wait(&cond, &m);
// Thread stuff here
pthread_mutex_unlock(&m);

Entonces ciertamente sería el caso de que si solo hay a lo sumo un receptor, y si cond_signal fuera la única cosa que pudiera despertarlo, entonces solo se despertaría cuando se estableciera la condición y, por lo tanto, no necesitaría un bucle. nos cubre por qué el segundo " si " no es cierto.

 15
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-03 10:03:29