Llamar a pthread_cond_signal sin bloquear mutex

83

Leí en alguna parte que deberíamos bloquear el mutex antes de llamar a pthread_cond_signal y desbloquear el mutex después de llamarlo:

La rutina pthread_cond_signal () se usa para señalar (o despertar) otro hilo que está esperando la variable de condición. Se debe llamar después de que el mutex esté bloqueado y debe desbloquearlo para que se complete la rutina pthread_cond_wait ().

Mi pregunta es: ¿no está bien llamar a los métodos pthread_cond_signal o pthread_cond_broadcast sin bloquear el mutex?

B Faley
fuente

Respuestas:

140

Si no bloquea el mutex en la ruta de código que cambia la condición y las señales, puede perder las activaciones. Considere este par de procesos:

Proceso A:

pthread_mutex_lock(&mutex);
while (condition == FALSE)
    pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);

Proceso B (incorrecto):

condition = TRUE;
pthread_cond_signal(&cond);

Luego considere este posible entrelazado de instrucciones, donde conditioncomienza como FALSE:

Process A                             Process B

pthread_mutex_lock(&mutex);
while (condition == FALSE)

                                      condition = TRUE;
                                      pthread_cond_signal(&cond);

pthread_cond_wait(&cond, &mutex);

El conditiones ahora TRUE, pero el proceso A está atascado esperando en la variable de condición: perdió la señal de activación. Si modificamos el proceso B para bloquear el mutex:

Proceso B (correcto):

pthread_mutex_lock(&mutex);
condition = TRUE;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

... entonces lo anterior no puede ocurrir; el despertar nunca se perderá.

(Tenga en cuenta que en realidad puede mover el pthread_cond_signal()mismo después de pthread_mutex_unlock(), pero esto puede resultar en una programación de subprocesos menos óptima, y ​​necesariamente ha bloqueado el mutex ya en esta ruta de código debido al cambio de la condición en sí).

coste y flete
fuente
3
@Nemo: Sí, en la ruta "correcta" pthread_signal_cond() se puede mover después del desbloqueo de mutex, aunque probablemente sea mejor no hacerlo. Quizás sea más correcto decir que en el punto donde está llamando pthread_signal_cond(), ya habrá necesitado haber bloqueado el mutex para modificar la condición en sí.
caf
@Nemo: Sí, el código está roto, por lo que está marcado como "incorrecto". Según mi experiencia, las personas que hacen esta pregunta a menudo consideran dejar el bloqueo por completo en la ruta de señalización. Quizás tu experiencia sea diferente.
Café
9
Por lo que vale, parece que la pregunta de si desbloquear antes o después de la señal no es trivial de responder. Desbloquear después asegura que un subproceso de menor prioridad no podrá robar el evento de uno de mayor prioridad, pero si no está utilizando prioridades, el desbloqueo antes de la señal reducirá la cantidad de llamadas al sistema / cambios de contexto y mejorar el rendimiento general.
R .. GitHub DEJA DE AYUDAR A ICE
1
@R .. Lo tienes al revés. El desbloqueo antes de la señal aumenta el número de llamadas al sistema y reduce el rendimiento general. Si envía la señal antes del desbloqueo, la implementación sabe que la señal no puede despertar un hilo (porque cualquier hilo bloqueado en el cv necesita el mutex para avanzar), lo que le permite usar la ruta rápida. Si envía una señal después del desbloqueo, tanto el desbloqueo como la señal pueden despertar los hilos, lo que significa que ambas son operaciones costosas.
David Schwartz
1
@Alceste_: Vea este texto en POSIX : "Cuando un hilo espera en una variable de condición, habiendo especificado un mutex particular para pthread_cond_timedwait()la pthread_cond_wait()operación o , se forma un enlace dinámico entre ese mutex y la variable de condición que permanece en efecto mientras en al menos un hilo está bloqueado en la variable de condición. Durante este tiempo, el efecto de un intento de cualquier hilo de esperar en esa variable de condición usando un mutex diferente no está definido " .
caf
49

Según este manual:

Las funciones pthread_cond_broadcast()o pueden ser llamadas por un subproceso, ya sea que actualmente posea o no el mutex que los subprocesos llaman o tienen asociado con la variable de condición durante sus esperas; sin embargo, si se requiere un comportamiento de programación predecible, entonces ese mutex será bloqueado por el hilo que llama o .pthread_cond_signal()pthread_cond_wait()pthread_cond_timedwait()pthread_cond_broadcast()pthread_cond_signal()

Dave Butenhof (autor de Programming with POSIX Threads ) explicó el significado de la declaración de comportamiento de programación predecible en comp.programming.threads y está disponible aquí .

hielo
fuente
6
+1 por vincular el correo de Dave Butenhof. Yo mismo siempre me pregunté sobre este tema, ahora lo sé ... Hoy aprendí algo importante. Gracias.
Nils Pipenbrinck
Si se requiere un comportamiento de programación predecible, coloque las declaraciones en un hilo en el orden que desee o utilice las prioridades del hilo.
Kaz
7

caf, en su código de muestra, el Proceso B modifica conditionsin bloquear primero el mutex. Si el Proceso B simplemente bloqueó el mutex durante esa modificación, y luego desbloqueó el mutex antes de llamar pthread_cond_signal, no habría ningún problema, ¿tengo razón en eso?

Creo intuitivamente que la posición de caf es correcta: llamar pthread_cond_signalsin tener el bloqueo mutex es una mala idea. Pero el ejemplo de caf no es realmente una evidencia que apoye esta posición; es simplemente evidencia en apoyo de la posición mucho más débil (prácticamente evidente por sí misma) de que es una mala idea modificar el estado compartido protegido por un mutex a menos que haya bloqueado ese mutex primero.

¿Alguien puede proporcionar algún código de muestra en el que la llamada pthread_cond_signalseguida de pthread_mutex_unlockun comportamiento correcto, pero la llamada pthread_mutex_unlockseguida de pthread_cond_signalun comportamiento incorrecto?

Quuxplusone
fuente
3
De hecho, creo que mi pregunta es un duplicado de esta , y la respuesta es "Está bien, puedes llamar totalmente a pthread_cond_signal sin tener el bloqueo mutex. No hay problema de corrección. Pero en implementaciones comunes, te perderás una optimización inteligente en el interior de pthreads, por lo que es un poco preferible llamar a pthread_cond_signal mientras mantiene el candado ".
Quuxplusone
Hice esta observación en el último párrafo de mi respuesta.
caf
Tiene un buen escenario aquí: stackoverflow.com/a/6419626/1284631 Tenga en cuenta que no afirma que el comportamiento sea incorrecto, solo presenta un caso en el que el comportamiento podría no ser el esperado.
user1284631
Es posible crear un código de muestra donde llamar a pthread_cond_signalafter pthread_mutex_unlockpuede resultar en una activación perdida porque la señal es detectada en el hilo "incorrecto", uno que se bloqueó después de ver el cambio en el predicado. Esto solo es un problema si la misma variable de condición se puede usar para más de un predicado y usted no la usa pthread_cond_broadcast, lo cual es un patrón raro y frágil de todos modos.
David Schwartz