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

  1. Tareas en Ada: Las tareas son unidades de ejecución concurrente.
  2. Declaración de Tareas: Cómo declarar y definir tareas en Ada.
  3. Comunicación entre Tareas: Métodos para que las tareas se comuniquen entre sí.
  4. 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:

procedure Main is
    Task1 : Worker;
begin
    Task1.Start_Work;
end Main;

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!

© Copyright 2024. Todos los derechos reservados