2.4.1 Principios de la sincronización: condición de carrera, sección crítica, exclusión mutua, atomicidad, abrazo mortal
Condición de carrera
Ocurre cuando dos o más procesos que están trabajando juntos comparten algún dato modificable (por ejemplo, una posición de memoria de lectura/escritura) que cada proceso puede leer o escribir y el resultado final depende del orden de ejecución. (Nota: Este problema no sucede en la monoprogramación).
La condición de carrera se produce cuando:
- Acceso concurrente: Dos o más procesos o hilos intentan acceder simultáneamente a un recurso compartido.
- Al menos una operación de escritura: Al menos uno de los procesos realiza una operación de escritura (modificación) en el recurso compartido.
- No hay sincronización: No se han implementado mecanismos de sincronización adecuados para controlar el acceso concurrente al recurso compartido.
Como resultado de la condición de carrera, el contenido del recurso compartido puede quedar en un estado inconsistente o incorrecto, ya que las operaciones de lectura y escritura pueden intercalarse de maneras no predecibles.
Para evitar condiciones de carrera, es necesario utilizar mecanismos de sincronización, como mutexes, semáforos o variables de condición, que permitan a los procesos o hilos coordinar su ejecución de manera que se evite el acceso simultáneo a los recursos compartidos. Estos mecanismos ayudan a garantizar la consistencia de los datos y a prevenir resultados inesperados en entornos concurrentes.
Figura 15. Ejecución en la computadora.
Exclusión mutua
Los procesos compiten por el uso de unos recursos limitados. Esos recursos pueden ser:
- Compartibles: Pueden ser empleados por varios procesos de forma concurrente. Ejemplos: CPU, archivos de lectura, áreas de memoria no modificables.
-
No compartibles: Su uso se restringe a un único proceso por:
- La naturaleza física del recurso: Ejemplos: Unidad de cinta magnética, lectora de tarjetas, impresora.
- Si el recurso lo usan otros procesos de forma concurrente, la acción de uno de ellos puede interferir en la de otro: Ejemplos: Archivo de escritura, posición de memoria.
La exclusión mutua consiste en asegurar que los recursos no compartibles sean accedidos por un único proceso a la vez.
Para evitar las condiciones de carrera necesitamos exclusión mutua, es decir, si un proceso está usando una variable o archivo compartido, el otro proceso será excluido de hacer lo mismo.
Figura 16. Secuencia de un proceso.
Secciones críticas y secciones críticas condicionales
Las secciones críticas o regiones críticas son fragmentos de programa que acceden a recursos no compartibles. Si dos procesos no están nunca en sus secciones críticas al mismo tiempo, se evitan las condiciones de carrera (esto no impide tener procesos paralelos que cooperen correctamente y eficientemente usando datos compartidos).
Las regiones críticas condicionales permiten que los procesos ejecuten sus secciones críticas sólo cuando se cumpla una determinada condición.
Cuando a un proceso no se le satisface la condición, se quedará suspendido en una cola especial en espera del cumplimiento de la condición. Así se consigue que, aunque un proceso espere en una condición, no evite que otros utilicen el recurso. Con ello conseguimos la sincronización además de la exclusión mutua a la sección crítica.
Condiciones que se requieren para evitar las condiciones de carrera:
- Dos procesos no pueden estar simultáneamente dentro de sus secciones críticas.
- No se hacen suposiciones sobre las velocidades de los procesos o el número de CPUs.
- Ningún proceso que se pare fuera de su sección crítica bloqueará a otros procesos.
- Ningún proceso esperará mucho tiempo para entrar en su sección crítica. Por ello, las secciones críticas deben ejecutarse tan rápido como sea posible. Además, un proceso no debe bloquearse dentro de su propia sección crítica (interbloqueo).
Figura 17. Procesador.
Atomicidad
Es uno de los conceptos fundamentales en la programación concurrente y en sistemas de bases de datos. Asegura que ciertas operaciones se realicen como una unidad atómica e indivisible, evitando condiciones de carrera y garantizando la consistencia de los datos en entornos donde múltiples procesos o hilos pueden acceder y modificar datos compartidos simultáneamente.
Algunos puntos clave sobre la atomicidad:
- Operaciones atómicas: Una operación atómica es aquella que se ejecuta de manera completa e indivisible. Ya sea que consista en una única instrucción o en un conjunto de instrucciones, una operación atómica se considera como si se ejecutara en un solo paso. Si se interrumpe una operación atómica, no dejará el sistema en un estado inconsistente.
- Evitar condiciones de carrera: La atomicidad es crucial para evitar condiciones de carrera, que ocurren cuando dos o más procesos intentan modificar un recurso compartido al mismo tiempo, resultando en comportamientos no deseados o inconsistentes.
- Mecanismos de sincronización: Los mecanismos de sincronización, como los mutexes, son utilizados para lograr la atomicidad. Al bloquear el acceso al recurso compartido mientras una operación se está realizando, se asegura que ningún otro proceso pueda interferir hasta que la operación se complete.
- Transacciones en bases de datos: En el contexto de las bases de datos, la atomicidad se refiere a las transacciones atómicas. Una transacción es un conjunto de operaciones que deben ejecutarse en su totalidad o no ejecutarse en absoluto. Si alguna parte de la transacción falla, se revierten todas las operaciones a un estado consistente, manteniendo la integridad de la base de datos.
- Rollback y Commit: Las transacciones en bases de datos suelen incluir dos conceptos relacionados: el commit (confirmación) y el rollback (reversión). El commit confirma que todas las operaciones de la transacción se han realizado con éxito, mientras que el rollback deshace todas las operaciones en caso de un fallo.
Figura 18. Archivos almacenados.
Abrazo mortal
El "abrazo mortal" o deadlock es una situación problemática en sistemas concurrentes donde dos o más procesos o hilos quedan atrapados indefinidamente porque cada uno está esperando que el otro libere un recurso necesario para avanzar. En otras palabras, hay un bloqueo circular de dependencias entre los procesos, y ninguno puede continuar su ejecución.
Características clave del deadlock:
- Bloqueo Mutuo: Cada proceso tiene un recurso que necesita para continuar y posee un recurso que el otro proceso necesita. Ambos procesos se bloquean mutuamente al retener los recursos necesarios para el otro.
- Espera Circular: Existe un ciclo en el que cada proceso está esperando que el siguiente libere un recurso. Esta cadena de espera circular es esencial para la existencia del deadlock.
- Recursos No Liberados: Los recursos que los procesos han adquirido no se liberan adecuadamente. Esto puede deberse a errores en el código, falta de sincronización o problemas de planificación.
Para evitar deadlocks y mitigar sus efectos, se implementan diversas estrategias:
- Asignación ordenada de recursos: Asignar recursos a los procesos de una manera ordenada y garantizar que todos los procesos sigan el mismo orden al adquirir recursos ayuda a prevenir deadlocks.
- iDetección y recuperación: Los sistemas operativos pueden implementar algoritmos de detección de deadlock para identificar si un deadlock ha ocurrido. Luego, se pueden tomar medidas, como liberar recursos o terminar procesos, para recuperarse de la situación.
- Jerarquía de recursos: Establecer una jerarquía de recursos y requerir que los procesos adquieran recursos en un orden específico puede prevenir deadlocks.
- Timeouts y liberación forzada: Si un proceso detecta que ha estado esperando demasiado tiempo por un recurso, puede liberar los recursos adquiridos y volver a intentar adquirirlos más tarde. Esto evita la permanencia indefinida en un deadlock.
- Exclusión mútua: Limitar el acceso a recursos críticos mediante el uso de mutexes o semáforos puede prevenir situaciones de deadlock, pero también puede reducir la concurrencia.
Figura 19. Jerarquía.
Reflexiona la siguiente pregunta que será tratada en la sesión de clase.
¿Cual ejemplo es implementado en aplicaciones de uso cotidiano que eviten las condiciones de carrera, abrazo mortal y exclusión mutua?