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
- Cuando el buffer se llena, la llenadora queda bloqueada en
wait(sHuecos): es la capacidad física la que detiene la producción, no un if. - Cuando el buffer se vacía, la empaquetadora queda bloqueada en
wait(sBotellas). - Los tiempos de llenado y empaquetado son aleatorios: fíjate en la ocupación media del buffer como indicador del equilibrio de la línea.
- El contador de bloqueos del productor mide cuántas veces la llenadora tuvo que esperar hueco.
Mapa semáforo → significado
| Semáforo | Inicial | Una unidad significa… |
|---|---|---|
sHuecos | 4 | una posición libre del buffer |
sBotellas | 0 | una botella llena disponible |
sMutex | 1 | permiso para tocar la cola del buffer |
Qué línea produce qué efecto
| Línea | Efecto 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
- Observa un bloqueo completo: llena el buffer y comprueba cómo la llenadora espera exactamente hasta el
signal(sHuecos)de la empaquetadora. - Imagina
N = 1: ¿qué cambiaría en el KPI de ocupación media? ¿Y siN = 100? - ¿Qué pasaría si la empaquetadora olvidara hacer
signal(sHuecos)? El simulador te lo muestra: prueba a razonarlo antes de mirarlo en Errores comunes.