Fundamentos
Variables de condición
Muchos hilos de planta necesitan esperar hasta que algo ocurra: que llegue una pieza,
que el depósito alcance el nivel, que se reciba la consigna por el canal. Consultar en bucle
(while (!sensor());) desperdicia CPU. La variable de condición permite dormir al hilo hasta que
otro le avise.
La espera activa y su coste
En los primeros ejercicios del cuaderno verás esperas por consulta:
while (!sensorEntrada()); // consulta el sensor miles de veces por segundo
cintaEntrada(0);
El hilo está «esperando», pero consume el 100 % de su núcleo. En un microcontrolador además impide dormir la CPU. La alternativa correcta es bloquearse y ser despertado por quien produce la condición.
wait + notify
Una std::condition_variable se usa siempre junto a un std::mutex y una variable
que describe la condición:
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool piezaLista = false; // la condición
void hPrensa() { // CONSUMIDOR de la señal
std::unique_lock<std::mutex> lk(mtx);
cv.wait(lk, [] { return piezaLista; }); // duerme hasta piezaLista == true
// aquí ya hay pieza: trabajar con ella
}
void hCinta() { // PRODUCTOR de la señal
// ... la cinta trae la pieza ...
{
std::lock_guard<std::mutex> lk(mtx);
piezaLista = true; // 1) cambia la condición
}
cv.notify_one(); // 2) despierta a un esperando
}
La secuencia siempre es la misma: cambiar la condición bajo el mutex y después
notificar. El que espera usa wait con predicado, que equivale a:
while (!predicado()) // comprueba la condición
cv.wait(lk); // si no se cumple, suelta el mutex y duerme
Por qué el predicado es obligatorio
- Despertares espurios: el estándar permite que
waitvuelva sin que nadie haya notificado. El predicado lo neutraliza. - Varios esperando: si dos hilos esperan y solo hay una pieza,
notify_onedespierta a uno; el otro debe volver a dormir al comprobar el predicado. - Señal antes de la espera: si la condición se cumplió antes de llegar al
wait, el predicado lo detecta y no se duerme.
notify_one vs notify_all
| Llamada | Despierta | Úsalo cuando… |
|---|---|---|
notify_one() | A un hilo esperando (si hay alguno) | La condición sirve para uno solo: una pieza, un hueco. |
notify_all() | A todos los hilos esperando | La condición puede servir a varios a la vez, o cambia de tipo (p. ej. parada general). |
Esperar con límite de tiempo
En una instalación real no se espera eternamente: si un sensor no responde en 5 s hay alarma. Las
variantes temporizadas devuelven false si se agota el tiempo:
std::unique_lock<std::mutex> lk(mtx);
if (!cv.wait_for(lk, std::chrono::seconds(5),
[] { return piezaLista; })) {
// TIMEOUT: la pieza no llegó → tratar como avería
}
Dónde las usarás
- Dentro de las colas bloqueantes: una condición para «no vacía» y otra para «no llena».
- Como base de los semáforos clásicos: un semáforo se implementa con mutex + condición + contador.
- Para eventos de planta: fin de lote, consigna recibida, permiso del operario.
Notificar sin haber cambiado la condición, o cambiarla sin mutex: el esperando puede despertar, ver que la condición sigue siendo falsa y volver a dormir… o perder la señal para siempre. La receta es invariable: mutex → cambiar condición → soltar → notify.