2008-09-16 15 views
41

¿Cuáles son los pros/contras de usar pthread_cond_wait o usar un semáforo? Estoy a la espera de un cambio de estado de esta manera:pthread_cond_wait versus semáforo

pthread_mutex_lock(&cam->video_lock); 
while(cam->status == WAIT_DISPLAY) { 
    pthread_cond_wait(&cam->video_cond, &cam->video_lock); 
} 
pthread_mutex_unlock(&cam->video_lock); 

Usando un semáforo correctamente inicializado, creo que podría hacerlo de esta manera:

while(cam->status == WAIT_DISPLAY) { 
    sem_wait(&some_semaphore); 
} 

¿Cuáles son los pros y los contras de cada método?

+0

me he dado cuenta de que "semáforo inicializado correctamente" está mal definida. ¿El semáforo está configurado en 1 o en 0? Diría que debería establecerse en 0. Entonces, ¿el semáforo protege el estado de la cámara-> o no? – Blaisorblade

+0

En el segundo fragmento, como se menciona en otras respuestas, se bloqueará el hilo mientras no se lanza mutex. Por lo tanto, su método 'sem_wait' nunca volverá porque ningún otro hilo puede adquirir el mutex y llamar a' sem_signal'. Sin embargo, no haré qué si lanzo mutex antes de 'sem_wait' y lo vuelvo a requerir después de esperar. Sé que estos pasos no son atómicos, entonces ¿qué pasará? – sevenkplus

Respuesta

58

Un semáforo se adapta limpiamente a un modelo productor-consumidor, aunque tiene otros usos. La lógica de su programa es responsable de garantizar que se realice la cantidad correcta de publicaciones para el número de esperas. Si publica un semáforo y nadie lo está esperando aún, entonces cuando lo hagan, continuarán de inmediato. Si su problema es tal que se puede explicar en términos del valor de conteo de un semáforo, entonces debería ser fácil de resolver con un semáforo.

Una variable de condición es un poco más tolerante en algunos aspectos. Puede, por ejemplo, usar cond_broadcast para despertar a todos los camareros, sin que el productor sepa cuántos hay. Y si cond_signal un condvar con nadie esperando, entonces no pasa nada. Esto es bueno si no sabes si va a haber un oyente interesado. También es la razón por la cual el oyente siempre debe verificar el estado con el mutex sostenido antes de esperar; si no lo hacen, pueden perder una señal y no despertarse hasta la siguiente (que podría ser nunca).

Por lo tanto, una variable de condición es adecuada para notificar a las partes interesadas que el estado ha cambiado: adquiere el mutex, cambia el estado, señaliza (o transmite) el condvar y libera el mutex. Si esto describe su problema, se encuentra en territorio condvar. Si diferentes oyentes están interesados ​​en diferentes estados, puedes transmitir y cada uno se despertará, descubrirá si han encontrado el estado que desean y, si no, esperarán nuevamente.

De hecho, es muy complicado intentar este tipo de cosas con un mutex y un semáforo. El problema surge cuando quieres tomar el mutex, verificar un estado y luego esperar en el semáforo para ver los cambios. A menos que pueda liberar atómicamente el mutex y esperar en el semáforo (que en pthreads no puede), termina esperando en el semáforo mientras mantiene el mutex. Esto bloquea el mutex, lo que significa que otros no pueden tomarlo para realizar el cambio que te interesa. Por lo tanto, tendrá la tentación de agregar otro mutex de una manera que dependa de sus requisitos específicos. Y tal vez otro semáforo. El resultado es un código generalmente incorrecto con condiciones de carrera dañinas.

Las variables de condición escapan a este problema, porque la llamada cond_wait libera automáticamente el mutex, liberándolo para su uso por otros. El mutex se recupera antes de que dev_wait regrese.

IIRC es posible implementar un tipo de condvar usando solo semáforos, pero si el mutex que está implementando para ir con el condvar es necesario tener trylock, entonces es un grave scratch, y las esperas temporizadas están fuera . No recomendado. Así que no asumas que cualquier cosa que puedas hacer con un condvar se puede hacer con semáforos. Además, por supuesto, los mutex pueden tener comportamientos agradables de los que carecen los semáforos, principalmente la prevención de inversión de prioridad.

0

En su segundo fragmento, obtiene el bloqueo una multitud de veces, nunca lo libera.

En general, el estado que está siguiendo puede ser completamente expresado por un semáforo, entonces puede usarlo. Una estructura de cerradura es más pequeña en tamaño y requiere menos operaciones atómicas para verificar/establecer/liberar.

De lo contrario, si el estado es complejo, y diferentes partes del código esperan en condiciones diferentes de la misma variable (por ejemplo, aquí usted quiere x < 10; allí desea y> x), use cond_wait.

+0

El segundo fragmento es correcto: un despertador siempre es causado por alguien que señala el semáforo. – Blaisorblade

+0

@Blaisorblade, el despertar es correcto, la comprobación de estado es no porque no está protegida. –

+0

Ese es un problema diferente al que describe esta respuesta: "en su segundo fragmento, obtiene el bloqueo muchas veces, nunca lo libera". Mencioné este problema en mi respuesta anterior (aunque no muy específicamente). Pero al volver a mirar el código, me di cuenta de que uno realmente no puede decir si el segundo fragmento es correcto o no, porque faltan demasiados detalles (ver mi comentario a la pregunta). Pero expresé incorrectamente mi comentario, debería haber dicho "el problema que mencionas no está allí, porque ...". – Blaisorblade

19

condicionales permiten hacer algunas cosas que los semáforos no lo hará.

Por ejemplo, supongamos que tiene algún código que requiere un mutex, llamado m. Sin embargo, necesita esperar hasta que otro subproceso finalice su tarea, por lo que espera en un semáforo llamado s. Ahora, cualquier hilo que necesita m está bloqueado y no podrá correr, a pesar de que el hilo que tiene m está esperando en s. Este tipo de situaciones se pueden resolver usando condicionales. Cuando espera en un condicional, se libera el mutex actualmente en espera, por lo que otros hilos pueden adquirir el mutex. Volviendo a nuestro ejemplo, supongamos que se usó el condicional c en lugar de s. Nuestro hilo ahora adquiere m, y luego esperas condicionales en c. Esto libera m para que otros hilos puedan continuar. Cuando c esté disponible, se vuelve a adquirir m, y nuestro hilo original puede continuar alegremente en su camino.

Las variables condicionales también le permiten dejar todos los hilos esperando en una variable condicional para proceder a través de pthread_cond_broadcast. Además, también le permite realizar una espera temporizada para que no termine esperando por siempre.

Por supuesto, a veces no necesita variables condicionales, por lo que dependiendo de sus requisitos, una u otra puede ser mejor.

+3

Esta es una buena respuesta. Para agregar a la descripción, lo que hace que los condicionales sean únicos en relación con la combinación descrita del mutex y el semáforo es que el condicional manipula el par mutex/semáforo atómicamente. –

4

El segundo fragmento es picante, no hagas eso.

Las otras respuestas tienen una buena discusión sobre los méritos relativos; Solo agregaré que pthread_cond_broadcast es una clara ventaja de las variables de condición.

Más allá de eso, estoy más acostumbrado a las variables de condición para eso, ya que son las que se usan en Java, incluso porque te ayudan a evitar carreras al verificar las banderas compartidas.

De hecho, en el segundo fragmento no tiene ningún bloqueo que proteja la lectura del estado de la cámara, por lo que se accede a través de una carrera de datos. La mayoría de las plataformas le permitirán salirse con la suya en este ejemplo en particular, pero eso tiene una semántica no definida, por POSIX y por el modelo de memoria de los próximos estándares de C/C++.

De hecho, una condición real de carrera es posible si otro subproceso asigna una nueva estructura de leva y sobrescribe la leva; el hilo de espera podría ver la actualización del puntero de la "leva" sin ver la inicialización del estado de la cámara->. De hecho, el segundo fragmento está pidiendo problemas, en este caso y en general.

http://www.hpl.hp.com/personal/Hans_Boehm/c++mm/

+0

De hecho, en el segundo fragmento, el propio semáforo podría estar protegiendo (o no) el indicador de estado (aunque no puedo imaginar un buen protocolo de bloqueo para eso). – Blaisorblade