Qué es una condición de carrera
Una condición de carrera ocurre cuando dos procesos o hilos cambian el mismo dato a la vez y el resultado depende de quién llega antes. Es uno de los fallos más difíciles de encontrar, porque el programa casi siempre funciona.
Piensa en dos cajas de un supermercado que suman una venta al mismo contador de ventas del día. Si las dos leen el contador a la vez, una venta se pierde.
Al final de esta lección
Nota
Puedo explicar una condición de carrera y un interbloqueo y cómo se evitan.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Hilo | una línea de ejecución dentro de un proceso |
| Condición de carrera | el resultado depende del orden en que se mezclan los pasos |
| Sección crítica | el trozo de código que toca datos compartidos |
| Cerrojo | una marca que solo puede tener un hilo a la vez |
| Interbloqueo | procesos que se esperan unos a otros para siempre |
| Grafo de espera | un dibujo con una flecha de cada proceso al que espera |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Sumar uno a un contador son tres pasos
La orden contador = contador + 1 parece un solo paso. El procesador la hace en tres: leer el valor, sumar uno y guardar el resultado.
El sistema puede cambiar de proceso entre dos pasos cualesquiera. Si dos hilos hacen la suma a la vez, sus pasos se mezclan, y a veces se pierde una de las sumas.
Ejemplo: dos hilos y un contador
| Paso | Hilo | Qué hace | Valor del contador | Copia del hilo |
|---|---|---|---|---|
| 1 | A | lee | 0 | A tiene 0 |
| 2 | B | lee | 0 | B tiene 0 |
| 3 | A | suma | 0 | A tiene 1 |
| 4 | B | suma | 0 | B tiene 1 |
| 5 | A | guarda | 1 | A tiene 1 |
| 6 | B | guarda | 1 | B tiene 1 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una suma se perdió y nadie lo notó
Dos hilos sumaron uno cada uno y el contador debería valer 2. Vale 1, porque B leyó el 0 antes de que A guardara su resultado.
Si A termina sus tres pasos antes de que B empiece, sale 2. El fallo aparece solo con ciertos órdenes, y por eso cuesta tanto reproducirlo.
Un cerrojo deja pasar un hilo
El trozo de código que toca el dato compartido es la sección crítica. Para protegerla se usa un cerrojo.
Un hilo toma el cerrojo antes de entrar y lo suelta al salir. Si otro hilo lo intenta mientras está tomado, espera. Así los tres pasos de la suma nunca se mezclan con los de otro.
Toma un cerrojo libre y haz algo dentro
flock ~/cerrojo echo dentroSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Intenta tomarlo mientras ya está tomado
flock -n ~/cerrojo -c 'flock -n ~/cerrojo echo otro || echo ocupado'Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
flock es un cerrojo sobre un archivo
La primera orden toma el cerrojo, escribe dentro y lo suelta al terminar. Funciona a la primera porque nadie más lo tenía.
La segunda toma el cerrojo y, dentro, intenta tomarlo de nuevo con -n, que significa no esperar. No lo consigue y flock falla. El || ejecuta entonces echo ocupado, y es lo único que se imprime.
Un interbloqueo es una espera en círculo
Un cerrojo arregla la carrera, pero crea otro riesgo. Si el proceso A tiene el cerrojo 1 y espera el 2, y B tiene el 2 y espera el 1, ninguno avanza nunca. Es un interbloqueo.
Para dibujarlo se usa el grafo de espera: una flecha de cada proceso al proceso que tiene lo que él espera. Un interbloqueo es un ciclo de flechas.
D espera, pero no está en el ciclo
A espera a B, B espera a C y C espera a A: ese ciclo es un interbloqueo y ninguno de los tres saldrá solo.
D espera a A, así que también está parado. Pero D no forma parte del ciclo: si se rompiera, D seguiría. Para romperlo hay que terminar uno de los tres procesos o quitarle lo que tiene.
Las cuatro condiciones del interbloqueo
| Condición | Qué significa | Cómo se rompe |
|---|---|---|
| Exclusión mutua | Un recurso lo usa uno solo | Compartirlo, si se puede |
| Retener y esperar | Se espera otro recurso sin soltar el actual | Pedirlos todos de una vez |
| Sin expropiación | Nadie puede quitar un recurso | Permitir quitarlo |
| Espera circular | Los procesos forman un ciclo | Pedir los cerrojos siempre en el mismo orden |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente cree que un programa bloqueado va solo muy lento. Un interbloqueo no va lento: no avanza y no gasta procesador. Esperar más no lo arregla. Hay que romper el ciclo, y lo mejor es evitarlo pidiendo los cerrojos en un orden fijo.