it-swarm-es.tech

pthread_cond_wait versus semáforo

¿Cuáles son las ventajas y desventajas de usar pthread_cond_wait o usando 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);

Usando 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?

45
shodanex

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 la cantidad de esperas. Si publica un semáforo y nadie lo está esperando todavía, entonces, si esperan, continúan de inmediato. Si su problema es tal que puede explicarse 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. Por ejemplo, puede 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 habrá un oyente interesado. También es la razón por la cual el oyente siempre debe verificar el estado con el mutex retenido antes de esperar; si no lo hacen, pueden perder una señal y no despertarse hasta la próxima (lo cual podría no ser nunca).

Por lo tanto, 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ñala (o transmite) el condvar y libera el mutex. Si esto describe su problema, está en territorio condvar. Si diferentes oyentes están interesados ​​en diferentes estados, simplemente puede transmitir y cada uno de ellos se despertará, determinará si han encontrado el estado que desean y, si no, espere nuevamente.

De hecho, es muy retorcido intentar este tipo de cosas con un mutex y un semáforo. El problema surge cuando desea tomar el mutex, verificar algún 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), terminará 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 le 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 generalmente un código 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 usen. El mutex se recupera antes de que cond_wait regrese.

IIRC es posible implementar un tipo de condvar usando solo semáforos, pero si se requiere que el mutex que está implementando para ir con el condvar tenga trylock, entonces es un rascador de cabeza serio, y las esperas temporizadas están fuera. No recomendado. Por lo tanto, no asuma que todo lo que puede hacer con un condvar se puede hacer con semáforos. Además, por supuesto, los mutexes pueden tener comportamientos agradables que carecen de semáforos, principalmente evitación de inversión de prioridad.

62
Steve Jessop

Los condicionales le permiten hacer algunas cosas que los semáforos no harán.

Por ejemplo, suponga que tiene algún código que requiere un mutex, llamado m. Sin embargo, debe esperar hasta que algún otro hilo haya terminado su tarea, por lo que espera en un semáforo llamado s. Ahora, cualquier subproceso que necesite m está bloqueado para ejecutarse, aunque el subproceso que tiene m está esperando en s. Este tipo de situaciones se pueden resolver utilizando condicionales. Cuando espera un condicional, se libera el mutex actualmente retenido, por lo que otros subprocesos pueden adquirir el mutex. Volvamos a nuestro ejemplo y supongamos que se usó el condicional c en lugar de s. Nuestro hilo ahora adquiere m, y luego condicional espera en c. Esto libera m para que otros hilos puedan continuar. Cuando c está disponible, m se vuelve a adquirir, y nuestro hilo original puede continuar alegremente en su camino.

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

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

19
freespace

El segundo fragmento es picante, no hagas eso.

Las otras respuestas tienen una discusión agradable de 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 condicionar las variables para eso, ya que son lo que usas en Java, incluso porque te ayudan a evitar las razas 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 indefinida, por POSIX y por el modelo de memoria de los siguientes estándares C/C++.

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

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

5
Blaisorblade

En tu segundo fragmento, obtienes el bloqueo muchas veces, nunca lo liberas.

En general, el estado en el que estás esperando puede ser completamente expresado por un semáforo, luego puedes usar eso. Una estructura de bloqueo es de menor tamaño y requiere menos operaciones atómicas para verificar/configurar/liberar.

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

0
Javier