Variable condicional vs semáforo

Respuestas:

207

Los candados se utilizan para la exclusión mutua. Cuando desee asegurarse de que un fragmento de código sea atómico, coloque un candado alrededor. Teóricamente, podría usar un semáforo binario para hacer esto, pero ese es un caso especial.

Los semáforos y las variables de condición se basan en la exclusión mutua que proporcionan los bloqueos y se utilizan para proporcionar acceso sincronizado a los recursos compartidos. Se pueden utilizar para fines similares.

Una variable de condición se usa generalmente para evitar la espera ocupada (bucle repetidamente mientras se verifica una condición) mientras se espera que un recurso esté disponible. Por ejemplo, si tiene un hilo (o varios hilos) que no pueden continuar hasta que una cola esté vacía, el enfoque de espera ocupada sería simplemente hacer algo como:

//pseudocode
while(!queue.empty())
{
   sleep(1);
}

El problema con esto es que está perdiendo el tiempo del procesador al hacer que este hilo verifique repetidamente la condición. ¿Por qué no, en cambio, tener una variable de sincronización que se pueda señalar para indicarle al hilo que el recurso está disponible?

//pseudocode
syncVar.lock.acquire();

while(!queue.empty())
{
   syncVar.wait();
}

//do stuff with queue

syncVar.lock.release();

Presumiblemente, tendrá un hilo en otro lugar que está sacando cosas de la cola. Cuando la cola está vacía, puede llamar syncVar.signal()para despertar un hilo aleatorio que está dormido syncVar.wait()(o generalmente también hay un método signalAll()o broadcast()para despertar todos los hilos que están esperando).

Generalmente uso variables de sincronización como esta cuando tengo uno o más subprocesos esperando en una sola condición en particular (por ejemplo, que la cola esté vacía).

Los semáforos se pueden usar de manera similar, pero creo que se usan mejor cuando tienes un recurso compartido que puede estar disponible y no disponible en función de un número entero de cosas disponibles. Los semáforos son buenos para situaciones de productor / consumidor donde los productores están asignando recursos y los consumidores los consumen.

Piense si tuviera una máquina expendedora de refrescos. Solo hay una máquina de refrescos y es un recurso compartido. Tiene un hilo que es un proveedor (productor) que es responsable de mantener la máquina almacenada y N hilos que son compradores (consumidores) que quieren sacar refrescos de la máquina. La cantidad de refrescos en la máquina es el valor entero que impulsará nuestro semáforo.

Cada hilo de comprador (consumidor) que llega a la máquina de refrescos llama al down()método del semáforo para tomar un refresco. Esto tomará un refresco de la máquina y reducirá el recuento de refrescos disponibles en 1. Si hay refrescos disponibles, el código seguirá pasando la down()declaración sin ningún problema. Si no hay refrescos disponibles, el hilo dormirá aquí esperando que se le notifique cuando los refrescos estén disponibles nuevamente (cuando haya más refrescos en la máquina).

El hilo del proveedor (productor) estaría esencialmente esperando que la máquina de refrescos esté vacía. El vendedor recibe una notificación cuando se saca el último refresco de la máquina (y uno o más consumidores están esperando potencialmente a sacar los refrescos). El proveedor reabastecería la máquina de refrescos con el up()método del semáforo , el número disponible de refrescos se incrementaría cada vez y, por lo tanto, se notificaría a los subprocesos de consumidores en espera de que hay más refrescos disponibles.

Los métodos wait()y signal()de una variable de sincronización tienden a estar ocultos dentro de las operaciones down()y up()del semáforo.

Ciertamente, existe una superposición entre las dos opciones. Hay muchos escenarios en los que un semáforo o una variable de condición (o un conjunto de variables de condición) podrían servir para sus propósitos. Tanto los semáforos como las variables de condición están asociados con un objeto de bloqueo que utilizan para mantener la exclusión mutua, pero luego brindan una funcionalidad adicional además del bloqueo para sincronizar la ejecución del hilo. Depende principalmente de usted descubrir cuál tiene más sentido para su situación.

Esa no es necesariamente la descripción más técnica, pero así es como tiene sentido en mi cabeza.

Brent escribe código
fuente
9
Gran respuesta, me gustaría agregar de otras respuestas: Semaphore se usa para controlar la cantidad de subprocesos que se ejecutan. Habrá un conjunto fijo de recursos. El recuento de recursos se reducirá cada vez que un hilo posea el mismo. Cuando el recuento de semáforos llega a 0, no se permite que otros subprocesos adquieran el recurso. Los subprocesos se bloquean hasta que otros subprocesos que poseen liberaciones de recursos. En resumen, la principal diferencia es ¿cuántos subprocesos pueden adquirir el recurso a la vez? Mutex: es UNO. Semáforo - su DEFINED_COUNT, (tantos como recuento de semáforos)
berkay
10
Solo para explicar por qué hay este bucle while en lugar de un simple if: algo llamado despertador spurios . Citando este artículo de wikipedia : "Una de las razones de esto es un despertar falso; es decir, un hilo puede despertarse de su estado de espera aunque ningún hilo haya señalado la variable de condición"
Vladislavs Burakovs
3
@VladislavsBurakovs ¡Buen punto! Creo que también es útil para el caso en el que una transmisión despierta más subprocesos de los que hay recursos disponibles (por ejemplo, la transmisión despierta 3 subprocesos, pero solo hay 2 elementos en la cola).
Brent escribe código
Me gustaría poder votar por tu respuesta hasta que la cola esté llena;) Respuesta perfecta. Este código podría ayudar a averiguar los semáforos csc.villanova.edu/~mdamian/threads/PC.htm
Mohamad-Jaafar NEHME
3
@VladislavsBurakovs Para aclarar un poco, la razón por la que la condición aún puede ser falsa para un hilo que acaba de despertarse (lo que resulta en un despertar falso) es que podría haber habido un cambio de contexto antes de que el hilo tuviera la oportunidad de verificar la condición nuevamente, donde algún otro hilo programado hizo que esa condición fuera falsa. Esta es una de las razones por las que conozco un despertar falso, no sé si hay más.
Máximo
52

Revelemos qué hay debajo del capó.

La variable condicional es esencialmente una cola de espera , que admite operaciones de espera de bloqueo y activación, es decir, puede poner un hilo en la cola de espera y establecer su estado en BLOQUEO, y sacar un hilo de él y establecer su estado en LISTO.

Tenga en cuenta que para usar una variable condicional, se necesitan otros dos elementos:

  • una condición (generalmente implementada mediante la verificación de una bandera o un contador)
  • un mutex que protege la condición

El protocolo entonces se convierte en,

  1. adquirir mutex
  2. comprobar condición
  3. bloquear y liberar mutex si la condición es verdadera, de lo contrario liberar mutex

El semáforo es esencialmente un contador + un mutex + una cola de espera. Y se puede utilizar tal cual sin dependencias externas. Puede usarlo como mutex o como variable condicional.

Por lo tanto, el semáforo puede tratarse como una estructura más sofisticada que la variable condicional, mientras que la última es más ligera y flexible.

pepino
fuente
mutex se puede ver como una variable de condiciones, su condición es si se mantiene o no.
宏杰 李
18

Los semáforos se pueden utilizar para implementar el acceso exclusivo a las variables, sin embargo, están destinados a ser utilizados para la sincronización. Los muttex, por otro lado, tienen una semántica que está estrictamente relacionada con la exclusión mutua: solo el proceso que bloqueó el recurso puede desbloquearlo.

Desafortunadamente, no puede implementar la sincronización con mutex, por eso tenemos variables de condición. También observe que con las variables de condición puede desbloquear todos los hilos en espera en el mismo instante usando el desbloqueo de transmisión. Esto no se puede hacer con semáforos.

Dacav
fuente
9

las variables de semáforo y condición son muy similares y se utilizan principalmente para los mismos propósitos. Sin embargo, existen pequeñas diferencias que podrían hacer que uno sea preferible. Por ejemplo, para implementar la sincronización de barrera no sería posible usar un semáforo, pero una variable de condición es ideal.

La sincronización de barrera es cuando desea que todos sus hilos esperen hasta que todos hayan llegado a una determinada parte de la función del hilo. esto se puede implementar al tener una variable estática que es inicialmente el valor del total de subprocesos decrecido por cada subproceso cuando alcanza esa barrera. esto significaría que queremos que cada hilo duerma hasta que llegue el último. ¡Un semáforo haría exactamente lo contrario! con un semáforo, cada subproceso seguiría ejecutándose y el último subproceso (que establecerá el valor del semáforo en 0) se suspenderá.

por otro lado, una variable de condición es ideal. cuando cada hilo llega a la barrera comprobamos si nuestro contador estático es cero. si no, configuramos el hilo para dormir con la función de espera de la variable de condición. cuando el último hilo llega a la barrera, el valor del contador se reducirá a cero y este último hilo llamará a la función de señal de variable de condición que despertará a todos los demás hilos.

Danielle
fuente
1

Archivo variables de condición bajo sincronización del monitor. En general, he visto semáforos y monitores como dos estilos de sincronización diferentes. Existen diferencias entre los dos en términos de cuántos datos de estado se mantienen inherentemente y cómo desea modelar el código, pero en realidad no hay ningún problema que pueda ser resuelto por uno pero no por el otro.

Tiendo a codificar hacia la forma del monitor; en la mayoría de los lenguajes en los que trabajo, eso se reduce a mutex, variables de condición y algunas variables de estado de respaldo. Pero los semáforos también harían el trabajo.

Justin R
fuente
2
Esta sería una mejor respuesta si explicara qué es "formulario de monitorización".
Steven Lu
0

El mutexy conditional variablesson heredados de semaphore.

  • Para mutex, el semaphoreusa dos estados: 0, 1
  • Para condition variablesel semaphore contador de usos.

Son como azúcar sintáctico

Agua
fuente
En la biblioteca estándar de C ++, todos son objetos de distrito, todos implementados utilizando API específicas de la plataforma. Ciertamente, un semáforo desbloqueará el número de veces señalado, la variable de condición puede ser señalada varias veces pero desbloquear solo una vez. Es por eso que wair toma un mutex como parámetro.
Doron