Laboratorio 2 · Buffer limitado

Línea de envasado

El patrón productor–consumidor en su forma pura: una llenadora produce botellas, una empaquetadora las retira, y entre medias un buffer de 4 posiciones protegido por tres semáforos: sHuecos, sBotellas y sMutex.

Qué observar

Mapa semáforo → significado

SemáforoInicialUna unidad significa…
sHuecos4una posición libre del buffer
sBotellas0una botella llena disponible
sMutex1permiso para tocar la cola del buffer

Qué línea produce qué efecto

LíneaEfecto en la planta
hEnvasadora:3 llenarBotella()La llenadora trabaja; aparece el chorro.
hEnvasadora:4 wait(sHuecos)Si el buffer está lleno (valor 0), la llenadora se bloquea.
hEnvasadora:6 buffer.push(b)Una botella aparece en un slot del buffer.
hEnvasadora:8 signal(sBotellas)Si la empaquetadora dormía esperando botella, despierta.
hEmpaquetadora:3 wait(sBotellas)Se bloquea si no hay botellas (buffer vacío).
hEmpaquetadora:5 buffer.pop()Desaparece una botella del buffer.
hEmpaquetadora:7 signal(sHuecos)Si la llenadora dormía esperando hueco, despierta.
hEmpaquetadora:8 empaquetar(b)La empaquetadora trabaja; cuenta de empaquetadas sube.

Código C++20 equivalente

#include <semaphore>
#include <mutex>
#include <queue>
#include <thread>

constexpr int N = 4;                          // capacidad del buffer

std::counting_semaphore<N> sHuecos(N);       // N huecos al empezar
std::counting_semaphore<N> sBotellas(0);     // ninguna botella
std::mutex sMutex;                           // protege la cola
std::queue<Botella> buffer;

void hEnvasadora() {
    while (true) {
        Botella b = llenarBotella();
        sHuecos.acquire();                // espera hueco libre
        {
            std::lock_guard lk(sMutex);   // región crítica
            buffer.push(b);
        }                                 // el mutex se libera aquí (RAII)
        sBotellas.release();              // una botella más disponible
    }
}

void hEmpaquetadora() {
    while (true) {
        sBotellas.acquire();              // espera botella llena
        Botella b;
        {
            std::lock_guard lk(sMutex);   // región crítica
            b = buffer.front();
            buffer.pop();
        }                                 // mutex liberado
        sHuecos.release();                // un hueco liberado
        empaquetar(b);
    }
}

int main() {
    std::jthread t1(hEnvasadora), t2(hEmpaquetadora);
}
⚠ Comparación con la simulación

Fíjate en la correspondencia uno a uno: acquire() ≡ wait y release() ≡ signal. En la simulación el wait(sMutex) aparece como línea explícita; aquí el lock_guard lo hace por RAII dentro del ámbito.

Experimentos propuestos

  1. Observa un bloqueo completo: llena el buffer y comprueba cómo la llenadora espera exactamente hasta el signal(sHuecos) de la empaquetadora.
  2. Imagina N = 1: ¿qué cambiaría en el KPI de ocupación media? ¿Y si N = 100?
  3. ¿Qué pasaría si la empaquetadora olvidara hacer signal(sHuecos)? El simulador te lo muestra: prueba a razonarlo antes de mirarlo en Errores comunes.