pthread_create - pthread_cond_wait versus semáforo
semaphore c++ (4)
¿Cuáles son los pros / contras de usar pthread_cond_wait
o usar un semáforo? Estoy esperando un cambio de estado como este:
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);
Utilizando un semáforo correctamente inicializado, creo que podría hacerlo así:
while(cam->status == WAIT_DISPLAY) {
sem_wait(&some_semaphore);
}
¿Cuáles son los pros y los contras de cada método?
En su segundo fragmento, obtiene el bloqueo una multitud de veces, nunca lo libera.
En general, el estado al que te refieres puede ser expresado completamente por un semáforo, entonces puedes 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 diferentes condiciones de la misma variable (por ejemplo, aquí quiere x <10; allí desea y> x), use cond_wait.
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 indulgente 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).
Entonces, una variable de condición es adecuada para notificar a las partes interesadas que el estado ha cambiado: usted 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 retorcido 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 llamar a cond_wait libera automáticamente el mutex, liberándolo para que otros lo utilicen. 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 para tener trylock, entonces es un serio scratch, y las esperas temporizadas están fuera. No recomendado. No asuma que todo lo que puede hacer con un condvar puede hacerse 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.
El segundo fragmento es picante, no hagas eso.
Las otras respuestas tienen una buena discusión sobre los méritos relativos; Solo pthread_cond_broadcast
que pthread_cond_broadcast
es una clara ventaja de las variables de condición.
Más allá de eso, estoy más acostumbrado a condicionar variables para eso, ya que son las que usas en Java, incluso porque te ayudan a evitar las 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, es posible una condición de carrera real 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.
Los condicionales te permiten hacer algunas cosas que los semáforos no.
Por ejemplo, supongamos que tiene algún código que requiere un mutex, llamado m
. Sin embargo, necesita esperar hasta que otro subproceso haya terminado su tarea, por lo que espera en un semáforo llamado s
. Ahora cualquier hilo que necesite m
está bloqueado para ejecutarse, incluso si 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ó c
condicional c
lugar de s
. Nuestro hilo ahora adquiere m
, y luego espera condicional en c
. Esto libera m
para que otros subprocesos 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 que todos los hilos que esperan una variable condicional procedan 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.