Semáforos

Patrones con semáforos

Con wait y signal se construyen unos pocos patrones que resuelven el 95 % de las sincronizaciones de una planta. Cada patrón se presenta con su esquema de semáforos y su valor inicial, que es lo que hay que aprender a decidir.

1 · Cita (rendezvous): «espérame aquí»

Dos hilos deben llegar a un punto antes de continuar. Ejemplo: el robot no puede empezar hasta que el hilo de seguridad haya terminado la comprobación de la barrera óptica.

Semaforo sListo(0);            // empieza en 0: evento pendiente

void hSeguridad() {
    comprobarBarreras();       // operación larga
    sListo.signal();           // «ya está»
}

void hRobot() {
    sListo.wait();             // espera la comprobación
    trabajar();
}

Para cita completa entre N hilos (todos esperan a todos) se usa una barrera.

2 · Orden entre etapas: «primero A, luego B»

Generalización del anterior para imponer secuencias. Cada flecha del diagrama de la instalación es un semáforo inicializado a 0:

Semaforo sLleno(0), sCalentado(0);

void hLlenado()    { llenarDeposito();  sLleno.signal(); }
void hCalentador() { sLleno.wait(); calentar(); sCalentado.signal(); }
void hMezclador()  { sCalentado.wait(); mezclar(); }

3 · Pool de recursos: «hay N disponibles»

Un semáforo contador con N permisos limita cuántos hilos usan un recurso compartido. Del cuaderno: un operario atendiendo 10 máquinas, o 7 robots máximo alrededor de una cuba.

Semaforo sMaquinas(10);        // 10 máquinas libres al arrancar

void hTrabajo() {
    sMaquinas.wait();          // reserva una máquina
    usarMaquina();
    sMaquinas.signal();        // la devuelve al terminar
}

El valor del semáforo en cada instante dice cuántas máquinas quedan libres: diagnóstico gratis.

4 · Exclusión mutua: «de uno en uno»

Semáforo binario inicializado a 1 como región crítica. En los ejercicios: sRobot (el robot de la célula), sPantalla (la única pantalla), sMensaje (el mensaje del panel). En C++ moderno se prefiere std::mutex + lock_guard, pero el razonamiento es idéntico:

Semaforo sRobot(1);

void muevePieza(int desde, int hasta) {
    sRobot.wait();             // pedimos el robot
    coge(desde);
    deja(hasta);
    sRobot.signal();           // lo devolvemos SIEMPRE
}
⚠ El signal olvidado

Si una ruta sale de la región crítica sin signal (un return anticipado, una excepción), el recurso queda secuestrado y la planta se para. En C++ usa lock_guard/unique_lock; si usas semáforos, estructura el código para que el signal esté siempre en el camino de salida.

5 · Pareja productor/consumidor (sp/sc)

El patrón central de toda línea de producción. Entre dos estaciones hay una posición (o buffer) de capacidad C, y se gobierna con dos semáforos:

Semaforo sp(C);        // huecos libres: empieza en C
Semaforo sc(0);        // elementos listos: empieza en 0

void productor() {
    sp.wait();             // ¿hay hueco?
    depositar(elemento);
    sc.signal();           // hay un elemento más
}

void consumidor() {
    sc.wait();             // ¿hay elemento?
    retirar(elemento);
    sp.signal();           // hay hueco de nuevo
}

Si la posición solo cabe una pieza (una prensa, una plataforma, una celda de entrada), C = 1. Si es un pulmón o almacén intermedio, C es su capacidad. Además, si el depósito es una estructura de datos compartida, se añade un tercer semáforo mutex para protegerla — el patrón completo está en Productor–consumidor y se puede tocar en el laboratorio de envasado.

6 · Limitación de ritmo: «no más de N por minuto»

Un hilo repone permisos a ritmo fijo y los consumidores los gastan: sirve para limitar arranques de motores, accesos a un horno, inyección de piezas…

Semaforo sTurnos(0);

void hReloj() {                          // repone un permiso cada 2 s
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(2));
        sTurnos.signal();
    }
}

void hInyector() {
    sTurnos.wait();                      // solo avanza si hay permiso
    inyectarPieza();
}

Cómo elegir el semáforo adecuado

PreguntaRespuesta → semáforo
¿Es un evento que ocurre una vez?Binario a 0, el productor hace signal.
¿Es un recurso de una sola unidad?Binario a 1 (mutex).
¿Hay N unidades del recurso?Contador a N.
¿Es un buffer entre estaciones?Pareja sp/sc con capacidad C.
¿N hilos deben esperarse entre sí?Barrera (semáforos + contador).