2011-12-21 11 views
104

Para citar la página del manual:¿Por qué pthread_cond_wait tiene activaciones espúreas?

Al utilizar variables de condición, siempre hay un predicado de Boole que involucra variables compartidas asociadas con cada condición de espera que es verdad si el hilo debe proceder. Pueden aparecer fallas espurias de las funciones pthread_cond_timedwait() o pthread_cond_wait(). Dado que el retorno de pthread_cond_timedwait() o pthread_cond_wait() no implica nada sobre el valor de este predicado, el predicado debe volver a evaluarse a partir de dicho retorno.

Por lo tanto, pthread_cond_wait puede regresar aunque no lo haya indicado. A primera vista al menos, eso parece bastante atroz. Sería como una función que devuelve aleatoriamente el valor incorrecto o que se devuelve aleatoriamente antes de llegar realmente a una declaración de devolución adecuada. Parece un error importante. Pero el hecho de que eligieron documentar esto en la página del manual en lugar de corregirlo parece indicar que hay una razón legítima por la cual pthread_cond_wait termina despertando de forma espuria. Presumiblemente, hay algo intrínseco sobre cómo funciona que lo hace para que eso no se pueda evitar. La pregunta es qué.

¿Por qué hace pthread_cond_wait return spouriously? ¿Por qué no puede garantizarse que solo se va a activar cuando se haya indicado correctamente? ¿Alguien puede explicar la razón de su comportamiento espurio?

+4

Me imagino que tiene algo que ver con regresar cada vez que el proceso capte una señal. La mayoría de * nixes no reinician una llamada de bloqueo después de que una señal lo interrumpe; simplemente establecen/devuelven un código de error que dice que se produjo una señal. – cHao

+0

@cHao: aunque tenga en cuenta que debido a que las variables de condición tienen * otras * razones para despertar espurios de todos modos, manejar una señal no es un error para 'pthread_cond_ (timed) wait':" Si se envía una señal ... el hilo se reanuda la espera de la variable de condición como si no se hubiera interrumpido, o devolverá cero debido a activación espuria ". Otras funciones de bloqueo indican 'EINTR' cuando se interrumpe por una señal (por ejemplo' 'leer'), o se requiere que se reanuden (por ejemplo' pthread_mutex_lock'). Entonces, si no hubiera otros motivos para el despertar espurio, 'pthread_cond_wait' podría haberse definido como cualquiera de esos. –

+3

Un artículo relacionado en Wikipedia: [Despertar falso] (http://en.wikipedia.org/wiki/Spurious_wakeup) – Palec

Respuesta

64

La siguiente explicación se da por David R. Butenhof en "Programming with POSIX Threads" (p 80).:

wakeups espurias pueden sonar extraño, pero en algunos sistemas multiprocesador, por lo que la condición de activación completamente predecible podría retardar sustancialmente toda condición operaciones variables

En la siguiente comp.programming.threads discussion, que se expande en el pensamiento detrás del diseño:

 
Patrick Doyle wrote: 
> In article , Tom Payne wrote: 
> >Kaz Kylheku wrote: 
> >: It is so because implementations can sometimes not avoid inserting 
> >: these spurious wakeups; it might be costly to prevent them. 

> >But why? Why is this so difficult? For example, are we talking about 
> >situations where a wait times out just as a signal arrives? 

> You know, I wonder if the designers of pthreads used logic like this: 
> users of condition variables have to check the condition on exit anyway, 
> so we will not be placing any additional burden on them if we allow 
> spurious wakeups; and since it is conceivable that allowing spurious 
> wakeups could make an implementation faster, it can only help if we 
> allow them. 

> They may not have had any particular implementation in mind. 

You're actually not far off at all, except you didn't push it far enough. 

The intent was to force correct/robust code by requiring predicate loops. This was 
driven by the provably correct academic contingent among the "core threadies" in 
the working group, though I don't think anyone really disagreed with the intent 
once they understood what it meant. 

We followed that intent with several levels of justification. The first was that 
"religiously" using a loop protects the application against its own imperfect 
coding practices. The second was that it wasn't difficult to abstractly imagine 
machines and implementation code that could exploit this requirement to improve 
the performance of average condition wait operations through optimizing the 
synchronization mechanisms. 
/------------------[ [email protected] ]------------------\ 
| Compaq Computer Corporation    POSIX Thread Architect | 
|  My book: http://www.awl.com/cseng/titles/0-201-63392-2/  | 
\-----[ http://home.earthlink.net/~anneart/family/dave.html ]-----/ 

75

hay por lo menos dos cosas 'de activación espuria' podría significar:

  • Un hilo bloqueado en pthread_cond_wait puede regresar de la llamada aunque no haya habido ninguna llamada para señalizar o transmitir en la condición.
  • Un hilo bloqueado en pthread_cond_wait regresa debido a una llamada para señalizar o transmitir, sin embargo después de readquirir el mutex se encuentra que el predicado subyacente ya no es verdadero.

Pero este último caso puede ocurrir incluso si la implementación de la variable de condición no permite el primer caso. Considere una cola de consumidor de productor y tres hilos.

  • El subproceso 1 acaba de quitar la cola de un elemento y lo liberó, y la cola ahora está vacía. El hilo está haciendo lo que haga con el elemento que adquirió en alguna CPU.
  • El subproceso 2 intenta quitar la cola de un elemento, pero encuentra que la cola está vacía cuando está marcada en mutex, llama a pthread_cond_wait y bloquea la llamada en espera de señal/emisión.
  • El subproceso 3 obtiene el mutex, inserta un nuevo elemento en la cola, notifica la variable de condición y libera el bloqueo.
  • En respuesta a la notificación del subproceso 3, el subproceso 2, que estaba esperando en la condición, está programado para ejecutarse.
  • Sin embargo, antes de que el subproceso 2 se las arregle para entrar en la CPU y tomar el bloqueo de cola, el subproceso 1 completa su tarea actual y vuelve a la cola para más trabajo. Obtiene el bloqueo de cola, verifica el predicado y descubre que hay trabajo en la cola. Continúa para quitar la cola del elemento que insertó el hilo 3, libera el bloqueo y hace lo que haga con el elemento que el hilo 3 puso en cola.
  • El subproceso 2 ahora se conecta a una CPU y obtiene el bloqueo, pero cuando comprueba el predicado, descubre que la cola está vacía. El subproceso 1 'robó' el elemento, por lo que el despertador parece espurio. El subproceso 2 necesita esperar nuevamente en la condición.

Así que como siempre debe comprobar el predicado en un bucle, no importa si las variables de condición subyacentes pueden tener otras clases de despertar espurios.

+13

sí. Esencialmente, esto es lo que sucede cuando se utiliza un evento en lugar de un mecanismo de sincronización con un conteo. Lamentablemente, parece que los semáforos POSIX, (en Linux de todos modos), también están sujetos a despertar Spurius. Me resulta un poco extraño que una falla de funcionalidad fundamental de las primitivas de sincronización simplemente se acepte como "normal" y se deba solucionar a nivel de usuario :(Es de suponer que los desarrolladores estarían de brazos abiertos si se documentara una llamada al sistema con una sección 'Segura segmentada espuria' o, tal vez 'conexión espuria a la URL incorrecta' o 'apertura espuria del archivo incorrecto'. –

+1

upvoted, bastante claro. – Alcott

+2

El escenario más común de un "despertar espurio" es muy probablemente el lado -efecto de una llamada a pthread_cond_broadcast() Digamos que tiene un conjunto de 5 subprocesos, dos se despiertan a la transmisión y hacen el trabajo. Los otros tres se despiertan y encuentran que el trabajo ya está hecho. Los sistemas de multiprocesador también pueden El código simplemente comprueba el predicado de nuevo, ve un estado no válido y vuelve a dormirse. En cualquier caso, al verificar el predicado se resuelve el problema. En general, los usuarios de IMO solucionan el problema. no debe usar mutexes y condicionales POSIX sin formato. – CubicleSoft

6

La sección "Despertares múltiples por señal de condición" en pthread_cond_signal tiene una implementación de ejemplo de pthread_cond_wait y pthread_cond_signal que implica wake-ups falsas.

+1

Creo que esta respuesta es incorrecta, hasta donde llega. La implementación de ejemplo en esa página tiene una implementación de "notificar a uno" que es equivalente a "notificar a todos"; pero no parece generar wakeups realmente * espurios. La única forma de que un hilo se active es por medio de otro hilo que invoque "notificar a todos" o por otro hilo que invoque el-cosa-etiquetada- "notificar a uno" -que-es-en-verdad- "notificar a todos". – Quuxplusone

Cuestiones relacionadas