Patrones clásicos
Pipeline y cintas
Una línea de producción es un pipeline natural: cada pieza pasa por las etapas 1, 2, 3… en orden, y cada etapa trabaja a la vez sobre piezas distintas. El truco para que el paralelismo sea real es que cada etapa sea un hilo y cada junta, una pareja productor/consumidor.
La instalación en acción
Una cadena de tres máquinas conectadas por cintas (colas): la etapa 2 es más lenta y hace de cuello de botella. Arranca la simulación y observa cómo las colas de su entrada se llenan y la presión sube hacia la tolva (backpressure).
Consejo: fíjate en qué hilo queda bloqueado primero cuando la línea se satura.
El esquema general
Para K etapas se necesitan K relaciones productor/consumidor: la de entrada de la primera etapa y la de cada junta. En el estilo clásico, dos vectores de semáforos:
// sp[i] = hueco libre a la entrada de la etapa i (inicial = capacidad)
// sc[i] = pieza presente a la entrada de la etapa i (inicial = 0)
Semaforo sp[K]; // todos a su capacidad
Semaforo sc[K]; // todos a 0
Cada etapa i ejecuta siempre el mismo esqueleto:
void hEtapa(int i) {
while (true) {
sc[i].wait(); // 1) espera la pieza de la etapa anterior
procesar(i); // 2) trabaja (la etapa anterior ya puede seguir)
sp[i + 1].wait(); // 3) espera hueco en la siguiente
transferir(i, i + 1); // 4) mueve la pieza
sp[i].signal(); // 5) libera su entrada
sc[i + 1].signal(); // 6) avisa a la siguiente etapa
}
}
El orden de los pasos 5 y 6 es indiferente, pero el paso 3 debe ir después del procesado: si la etapa esperara hueco para soltar antes de terminar su trabajo, la pieza anterior quedaría retenida y el pipeline perdería paralelismo.
El transfer compartido: región crítica
En muchas máquinas (ej61 del cuaderno) un único sistema transfer mueve las piezas entre posiciones. Como lo necesitan todas las etapas a la vez, es región crítica:
Semaforo sTransfer(1); // el transfer es de uno en uno
void mueve(int origen, int destino) {
sTransfer.wait();
transfiere(origen, destino);
sTransfer.signal();
}
Igual se trata cualquier recurso de transporte único: el robot de la célula FMS, la grúa del pulmón, el carro electroimán entre cintas.
Cintas transportadoras entre etapas
Cuando entre dos máquinas hay una cinta, la cinta también tiene su hilo, que hace de consumidor de la máquina anterior y productor de la siguiente:
void hCinta(int c) {
while (true) {
cinta(c, 0); // parada por defecto (ahorro)
sCintaP[c].wait(); // ¿puedo recibir? (hueco en la cinta)
sCintaC[c - 1].wait(); // ¿hay pieza esperándome?
cinta(c, 1); // en marcha
while (!barrera(c + 1)); // hasta que la pieza llegue al final
cinta(c, 0); // parar
sMaquinaC[c].signal(); // avisa a la máquina de delante
sMaquinaP[c - 1].signal(); // libera a la máquina de detrás
}
}
El while (!barrera(c+1)); es una espera por sondeo al hardware; en un sistema con semáforos
ligados a interrupciones se sustituiría por un wait. El cuaderno lo deja como sondeo porque
modela la lectura directa del sensor.
Ritmo y cuello de botella
El throughput de un pipeline lo marca su etapa más lenta. Si la etapa 2 tarda el doble, las etapas 1 y 3
pasarán la mitad del tiempo bloqueadas en sp[2].wait() / sc[1].wait(). Consecuencias
de diseño:
- Los buffers intermedios no aumentan el ritmo sostenido; solo absorben variaciones puntuales.
- Si una etapa es lenta y cara de mejorar, se duplica (dos máquinas en paralelo con un repartidor), lo que se modela con un semáforo contador de máquinas libres.
- La ocupación de las colas indica dónde está el cuello de botella: se ve en el laboratorio de colas.