En este módulo, exploraremos uno de los aspectos más poderosos y distintivos de Ada: su capacidad para manejar tareas y concurrencia. Ada fue diseñado con la concurrencia en mente, lo que lo hace especialmente adecuado para aplicaciones en tiempo real y sistemas embebidos.
Conceptos Clave
- Tareas en Ada: Las tareas son unidades de ejecución concurrente.
- Declaración de Tareas: Cómo declarar y definir tareas en Ada.
- Comunicación entre Tareas: Métodos para que las tareas se comuniquen entre sí.
- Sincronización: Mecanismos para sincronizar tareas y evitar condiciones de carrera.
Declaración de Tareas
En Ada, una tarea se declara de manera similar a un procedimiento, pero con la palabra clave task
. Aquí hay un ejemplo básico:
task type Worker is entry Start_Work; end Worker; task body Worker is begin accept Start_Work do -- Código para iniciar el trabajo end Start_Work; -- Código de la tarea end Worker;
Explicación del Código
- task type Worker: Declara un tipo de tarea llamado
Worker
. - entry Start_Work: Declara una entrada que puede ser llamada para iniciar el trabajo.
- task body Worker: Define el cuerpo de la tarea
Worker
. - accept Start_Work: Bloquea la tarea hasta que
Start_Work
sea llamado.
Creación y Ejecución de Tareas
Para crear y ejecutar una tarea, simplemente declaramos una instancia de la tarea y llamamos a su entrada:
Explicación del Código
- Task1 : Worker: Crea una instancia de la tarea
Worker
. - Task1.Start_Work: Llama a la entrada
Start_Work
para iniciar la tarea.
Comunicación entre Tareas
Ada proporciona varias formas de comunicación entre tareas, incluyendo entradas y mensajes. Aquí hay un ejemplo de cómo dos tareas pueden comunicarse usando entradas:
task type Sender is entry Send_Message(Msg : in String); end Sender; task body Sender is begin accept Send_Message(Msg : in String) do -- Enviar mensaje end Send_Message; end Sender; task type Receiver is entry Receive_Message(Msg : out String); end Receiver; task body Receiver is Msg : String(1..100); begin accept Receive_Message(Msg : out String) do -- Recibir mensaje end Receive_Message; end Receiver;
Explicación del Código
- Sender: Tarea que envía un mensaje.
- Receiver: Tarea que recibe un mensaje.
- accept Send_Message: Bloquea hasta que
Send_Message
sea llamado. - accept Receive_Message: Bloquea hasta que
Receive_Message
sea llamado.
Sincronización
La sincronización es crucial para evitar condiciones de carrera y asegurar que las tareas se ejecuten en el orden correcto. Ada proporciona varios mecanismos para la sincronización, incluyendo los objetos protegidos y las barreras de entrada.
Ejemplo de Sincronización con Objetos Protegidos
protected type Shared_Data is procedure Write(Data : in Integer); function Read return Integer; private Value : Integer := 0; end Shared_Data; protected body Shared_Data is procedure Write(Data : in Integer) is begin Value := Data; end Write; function Read return Integer is begin return Value; end Read; end Shared_Data; task type Writer is end Writer; task body Writer is Shared : Shared_Data; begin Shared.Write(10); end Writer; task type Reader is end Reader; task body Reader is Shared : Shared_Data; Value : Integer; begin Value := Shared.Read; end Reader;
Explicación del Código
- protected type Shared_Data: Declara un tipo protegido que asegura el acceso exclusivo a los datos compartidos.
- procedure Write: Procedimiento para escribir datos.
- function Read: Función para leer datos.
- Writer: Tarea que escribe datos.
- Reader: Tarea que lee datos.
Ejercicio Práctico
Ejercicio
Crea un programa en Ada que tenga dos tareas: una tarea Producer
que genere números enteros y los envíe a una tarea Consumer
que los imprima en la consola.
Solución
task type Producer is entry Produce(Number : in Integer); end Producer; task body Producer is begin for I in 1..10 loop accept Produce(Number : in Integer) do -- Producir número end Produce; end loop; end Producer; task type Consumer is entry Consume(Number : in Integer); end Consumer; task body Consumer is Number : Integer; begin for I in 1..10 loop accept Consume(Number : in Integer) do -- Consumir número Put_Line("Consumed: " & Integer'Image(Number)); end Consume; end loop; end Consumer; procedure Main is Prod : Producer; Cons : Consumer; begin for I in 1..10 loop Prod.Produce(I); Cons.Consume(I); end loop; end Main;
Explicación del Código
- Producer: Tarea que produce números.
- Consumer: Tarea que consume números.
- Main: Procedimiento principal que coordina la producción y el consumo.
Conclusión
En esta sección, hemos aprendido sobre las tareas y la concurrencia en Ada. Hemos visto cómo declarar y definir tareas, cómo comunicarse entre ellas y cómo sincronizarlas. Estos conceptos son fundamentales para escribir programas concurrentes y en tiempo real en Ada. En el próximo módulo, exploraremos los objetos protegidos y otros mecanismos avanzados de concurrencia.
¡Sigue practicando y experimentando con tareas para dominar la concurrencia en Ada!
Curso de Programación en Ada
Módulo 1: Introducción a Ada
Módulo 2: Conceptos Básicos
- Variables y Tipos de Datos
- Operadores y Expresiones
- Estructuras de Control
- Bucles en Ada
- Subprogramas: Procedimientos y Funciones
Módulo 3: Tipos de Datos Avanzados
Módulo 4: Programación Modular
Módulo 5: Concurrencia y Programación en Tiempo Real
Módulo 6: Temas Avanzados
Módulo 7: Mejores Prácticas y Optimización
- Estilo de Código y Mejores Prácticas
- Depuración y Pruebas
- Optimización del Rendimiento
- Consideraciones de Seguridad