Referencia
Errores comunes
Siete fallos que aparecen una y otra vez en el control concurrente de instalaciones. Cada uno con su síntoma, su causa y la regla que lo evita.
1 · La señal perdida
Síntoma: un hilo se queda bloqueado para siempre en un wait, sin motivo aparente.
Causa: el productor hace signal antes de que el consumidor llegue al
wait. Si el semáforo está inicializado a 0, ese signal se «guarda»… pero si el código usa una
condición booleana con un mutex mal ordenado, el aviso puede perderse.
Regla: con variables de condición, cambia la condición bajo el mutex y notifica después; el que espera usa siempre predicado (ver Variables de condición). Con semáforos, inicialízalos a 0 si el evento aún no ha ocurrido.
2 · wait sin predicado
// MAL: puede volver sin que la condición se cumpla (despertar espurio)
cv.wait(lk);
// BIEN: el predicado se comprueba antes, después y en cada despertar
cv.wait(lk, [] { return piezaLista; });
Regla: wait con predicado siempre, o verifica la condición en bucle.
3 · Deadlock por orden de los wait
Síntoma: la planta se congela; ningún hilo avanza (el laboratorio lo marca como INTERBLOQUEO).
// hilo A // hilo B
sMutex.wait(); sRobot.wait();
sRobot.wait(); // nunca podrá sMutex.wait(); // nunca podrá
Regla: todos los hilos piden los recursos en el mismo orden, o usa
std::scoped_lock. En semáforos: primero el recurso «aguas arriba», luego el «aguas abajo», nunca
dentro de una región crítica esperar por otro permiso que otro pueda estar reteniendo.
4 · Busy-wait en vez de bloqueo
Síntoma: la CPU está al 100 % con la planta parada; el hilo se traga un núcleo consultando un sensor o una variable.
// MAL: el hilo quema CPU consultando en bucle
while (!piezaLista) {}
// BIEN: bloquea hasta que llegue la señal
sLista.wait();
Regla: toda espera de duración impredecible se hace con semáforo, condición o cola bloqueante; el sondeo solo se acepta en lecturas de hardware de tiempo real de corta duración.
5 · El mutex que no protege
Síntoma: carreras y valores incoherentes a pesar de usar mutex.
Causa: algún hilo accede a los datos compartidos sin tomar el mutex, o el mutex protege solo una parte del grupo de datos (por ejemplo, la tabla de posiciones pero no el contador asociado).
Regla: encapsula los datos en un tipo (la ColaBloqueante es el ejemplo): el
mutex y los datos viajan juntos y no hay forma de olvidarlo.
6 · Mantener candado durante una espera larga
Síntoma: toda la instalación se ralentiza; los hilos se encolan en un mutex que uno de ellos retiene mientras hace E/S o espera por otro recurso.
Regla: región crítica mínima: copia los datos que necesites, suelta, y trabaja fuera.
Nunca hagas sleep_for ni esperes un semáforo dentro de una región crítica.
7 · join olvidado (y el hilo suicida)
Síntoma: el programa aborta con terminate al salir de main, o un
hilo que referencia variables que ya se destruyeron (uso de memoria liberada).
Regla: usa std::jthread y que los hilos se unan solos; los datos que un hilo
toca deben vivir más que el hilo (declarados antes, destruidos después del join).
Tabla-resumen
| Error | Cómo detectarlo | Cómo evitarlo |
|---|---|---|
| Señal perdida | Hilo bloqueado indefinidamente | Predicado + condición bajo mutex |
| wait sin predicado | Despertares espurios, fallos intermitentes | wait(lk, pred) |
| Deadlock | Congelación total | Orden fijo, scoped_lock |
| Busy-wait | CPU al 100 % parado | Semáforo / condición / cola |
| Mutex «parcial» | Carreras persistentes | Encapsular datos |
| Candado largo | Línea ralentizada | Región crítica mínima |
| join olvidado | Abortos, uso de memoria liberada | std::jthread |