Patrones clásicos
Productor–consumidor
El patrón que aparece en cada junta de una planta: una estación produce, la siguiente consume, y entre medias un buffer de capacidad finita. Se gobierna con dos semáforos (huecos y elementos) y, si el buffer es una estructura compartida, un mutex.
La instalación en acción
Un horno de cocción separa la preparación del enfriado: pulsa Ejecutar y observa cómo la carga se bloquea cuando el horno está lleno y la descarga cuando está vacío, y cómo el mutex protege la cola del horno.
Consejo: usa ⏭ Paso para ver qué línea de cada hilo se ejecuta y por qué.
El problema
- El productor no puede depositar si el buffer está lleno: debe esperar un hueco.
- El consumidor no puede retirar si el buffer está vacío: debe esperar un elemento.
- El acceso al buffer debe ser mutuamente excluyente si se implementa sobre memoria compartida.
Solución con semáforos (estilo clásico)
constexpr int N = 5; // capacidad del buffer
Semaforo sHuecos(N); // N huecos libres al empezar
Semaforo sElementos(0); // ningún elemento al empezar
Semaforo sMutex(1); // protege el buffer
std::queue<Pieza> buffer;
void hProductor() {
while (true) {
Pieza p = fabricar();
sHuecos.wait(); // espera hueco
sMutex.wait(); // región crítica
buffer.push(p);
sMutex.signal();
sElementos.signal(); // un elemento disponible
}
}
void hConsumidor() {
while (true) {
sElementos.wait(); // espera elemento
sMutex.wait(); // región crítica
Pieza p = buffer.front();
buffer.pop();
sMutex.signal();
sHuecos.signal(); // un hueco liberado
procesar(p);
}
}
El productor espera primero el hueco y luego el mutex, nunca al revés. Si tomara el mutex y luego esperara hueco dentro de la región crítica, el consumidor jamás podría entrar a vaciar: interbloqueo. Esta es la causa número uno de deadlock en buffers.
Solución moderna con cola bloqueante
Con la clase ColaBloqueante de la sección Colas, los tres semáforos
quedan encapsulados y el código de estación se reduce a push/pop:
ColaBloqueante<Pieza> buffer(N);
void hProductor() {
while (true) buffer.push(fabricar()); // bloquea si llena
}
void hConsumidor() {
while (true) procesar(buffer.pop()); // bloquea si vacía
}
Varios productores, varios consumidores
El patrón escala sin cambios: cualquier número de hilos puede hacer push y pop de la misma cola, porque la propia cola serializa el acceso. Ejemplo típico: varias cintas de entrada alimentan un pulmón común del que tiran varios robots.
Interpretación física
| Concepto del patrón | En la instalación |
|---|---|
| Buffer | Pulmón, almacén intermedio, tramo de cinta, mesa de transferencia. |
| Capacidad N | Número de posiciones físicas reales. |
| Productor bloqueado | Estación aguas arriba parada porque no hay dónde dejar. |
| Consumidor bloqueado | Estación aguas abajo en espera de material (hambre de pieza). |
| Ocupación media | Indicador de equilibrio de la línea (KPI del laboratorio). |
La línea de envasado implementa exactamente este patrón: observa los
semáforos sHuecos, sBotellas y sMutex mientras la llenadora y la
empaquetadora van a ritmos distintos.