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

ErrorCómo detectarloCómo evitarlo
Señal perdidaHilo bloqueado indefinidamentePredicado + condición bajo mutex
wait sin predicadoDespertares espurios, fallos intermitenteswait(lk, pred)
DeadlockCongelación totalOrden fijo, scoped_lock
Busy-waitCPU al 100 % paradoSemáforo / condición / cola
Mutex «parcial»Carreras persistentesEncapsular datos
Candado largoLínea ralentizadaRegión crítica mínima
join olvidadoAbortos, uso de memoria liberadastd::jthread