Introduction to RTOS Part 6 - Mutex | Digi-Key Electronics
Condiciones de Carrera en Programación Concurrente
Introducción a las Condiciones de Carrera
- Se presenta una condición de carrera clásica en pseudocódigo, un problema común en la programación concurrente. Este episodio profundiza en cómo surgen problemas al trabajar con recursos compartidos, como variables globales.
Problemas con Recursos Compartidos
- Al modificar y escribir un recurso compartido, no se puede controlar cuándo otros hilos interrumpirán y tratarán de hacer lo mismo, lo que puede resultar en comportamientos erráticos. Herramientas como semáforos y mutexes pueden ayudar a solucionar estos problemas.
Ejemplo Simple de Condición de Carrera
- Se introduce un ejemplo simple donde dos tareas (A y B) intentan acceder a una variable global. Si la actualización no se completa en un solo ciclo de instrucción, pueden surgir problemas.
- La condición de carrera es un fenómeno donde el comportamiento del sistema depende del tiempo de eventos incontrolables. En este caso, ambas tareas incrementan indefinidamente una variable global.
Funcionamiento Interno del Problema
- Incrementar una variable en memoria requiere varios ciclos de instrucción. Un diagrama ilustra cómo las tareas A y B interactúan con la variable global que comienza en cero.
- Si la tarea A obtiene el valor actual (2), pero antes de escribirlo nuevamente se interrumpe por la tarea B, esta última podría sobrescribir el valor antes que A complete su operación.
Consecuencias Prácticas
- Esto resulta en que ambas tareas incrementen la variable global solo una vez cuando deberían haberla incrementado dos veces; esto es una clara manifestación de una condición de carrera.
- La forma en que se incrementa la variable depende del orden exacto de ejecución, lo cual es impredecible debido a las interrupciones entre tareas.
Soluciones Propuestas
- Se menciona un programa simple para demostrar esta condición utilizando FreeRTOS. Dos tareas ejecutan funciones que leen e incrementan una variable global aleatoriamente.
- El código incluye esperas aleatorias para simular condiciones más realistas donde los hilos compiten por acceso a recursos compartidos.
Secciones Críticas y Exclusión Mutua
- La sección crítica es aquella parte del código que trabaja con recursos compartidos y debe completarse completamente antes que otra tarea pueda entrar.
- La exclusión mutua es el concepto mediante el cual se evita que otras tareas accedan a estas secciones críticas mientras están siendo utilizadas por otra tarea.
Herramientas para Manejar Exclusión Mutua
- Las colas permiten pasar mensajes entre hilos y pueden usarse para crear sistemas de bloqueo donde un hilo indica a otros cuándo pueden entrar a una sección crítica.
Introducción a Mutex y Semáforos en FreeRTOS
Conceptos Básicos de Mutex y Semáforos
- Un lock funciona para todos los procesos del sistema; en un entorno FreeRTOS, un lock es equivalente a un mutex. Un semáforo es similar a un mutex pero incluye un contador que permite que un número limitado de hilos acceda a una sección crítica al mismo tiempo.
- Se explorarán los semáforos en la próxima lección. Por ahora, se utilizará el ejemplo de una cafetería con un solo baño para ilustrar cómo usar un mutex.
- El baño representa el recurso compartido; solo una persona (o hilo) puede acceder a él a la vez. La llave actúa como nuestro mutex: se toma cuando se desea usar el recurso y se devuelve al terminar.
Ejemplo Práctico de Uso de Mutex
- En este ejemplo, ambos hilos tienen acceso a un objeto compartido (mutex), representado como un valor booleano (0 o 1). La tarea A verifica si el mutex está disponible y lo toma si es así.
- Esta acción debe ser atómica; no puede ser interrumpida por otras tareas mientras se verifica y toma el mutex. Algunas arquitecturas tienen instrucciones especiales que permiten esto en un ciclo de instrucción.
- Si la tarea A obtiene el mutex, puede realizar acciones críticas, como copiar valores de variables globales. Si la tarea B intenta acceder al mismo tiempo, fallará porque el mutex no está disponible.
Comportamiento del Scheduler
- Cuando el scheduler devuelve la ejecución a la tarea A, esta completa su trabajo en la sección crítica e incrementa la variable global antes de devolver el mutex.
- Al ejecutarse nuevamente, la tarea B encuentra que el mutex está disponible y puede proceder con su propia sección crítica sin interferencias externas.
Implementación en FreeRTOS
- Para implementar esto en FreeRTOS, primero hay que incluir el archivo de encabezado del semáforo ya que los semáforos y los mutex son conjuntos separados de archivos fuente.
- Se crea un mutex global utilizando
xSemaphoreCreateMutex, asignando su valor devuelto a nuestro manejador global del mutex.
Manejo del Mutex en Tareas
- En las funciones de tareas, intentamos tomar el mutex usando
xSemaphoreTake. Si no se puede obtener dentro del tiempo especificado (en ticks), retornapdFALSE.
- Para crear una tarea no bloqueante, podemos usar 0 como parámetro para que retorne inmediatamente si no tiene acceso al mutex. Esto permite hacer algo útil mientras espera.
Desafío Propuesto
- Se plantea evitar pasar parámetros locales al crear tareas debido a problemas potenciales con variables fuera del alcance.
- Se propone "hackear" este problema usando punteros para almacenar valores leídos desde terminales seriales y pasarlos correctamente entre tareas sin perder contexto.
Uso de Mutex y Semáforos en Programación
Manejo del Alcance de Variables
- El valor de la variable
numse pierde cuando el programa sale del alcance antes de que la tarea comience, lo que resulta en un valor predeterminado de cero.
- Se sugiere utilizar un mutex para evitar que la configuración salga antes de que se lea la variable
parameters.
Implementación en Tareas LED
- En la tarea del LED parpadeante, se menciona que el uso del mutex es una solución improvisada y no es el uso adecuado.
- Se compara el uso del mutex con un semáforo, indicando que este último sería más apropiado para señalar a otra tarea.