Programación Asíncrona

Asincronía:

🌀 Definición: Ejecución de tareas de forma que no se bloqueen entre sí, permitiendo que el hilo principal continúe ejecutando otras operaciones.

🔄 Importancia: Permite mejorar el rendimiento y la capacidad de respuesta de las aplicaciones, especialmente en operaciones que dependen de recursos externos como I/O o solicitudes de red.

Task en Programación:

📦 Definición: Representa una operación asíncrona en curso. No es el resultado final, sino un marcador de progreso. (Sustituimos void por Task , y si el método devuelve algo sera Task<Algo>)

🛠 Uso: Permite consultar el estado de la operación (en curso, completada, etc.) y obtener el resultado una vez completada.

Palabras Clave: Async y Await:

🚦 Async: Indica que una función puede contener operaciones asíncronas. Permite que la función retorne una Task.

Await: Se utiliza para esperar el resultado de una operación asíncrona sin bloquear el hilo de ejecución.

🔹 Ejemplos Prácticos: Preparar un Desayuno

  • Ejecución Síncrona:

    • Descripción: Se realiza una tarea tras otra. Cada tarea debe completarse antes de iniciar la siguiente.

    • 🍳 Ejemplo: Primero hervir agua para el café, luego freír los huevos, después cocinar el bacon, y finalmente tostar el pan.

  • Ejecución Paralela:

    • Descripción: Varias tareas se ejecutan al mismo tiempo en diferentes hilos o procesadores.

    • 🍳 Ejemplo: Mientras se hierve el agua en un hilo, simultáneamente en otro hilo se fríen los huevos.

  • Ejecución Asíncrona:

    • Descripción: Se inicia una tarea y mientras esta se completa, se pueden realizar otras tareas.

    • 🍳 Ejemplo: Poner a hervir agua y mientras se calienta, empezar a freír los huevos. No es necesario esperar a que el agua hierva para comenzar con los huevos.

En código se vería algo así : Sincrono :

class Program
{
    static void Main(string[] args)
    {
        Coffee cup = PourCoffee();
        Console.WriteLine("coffee is ready");

        Egg eggs = FryEggs(2);
        Console.WriteLine("eggs are ready");

        Bacon bacon = FryBacon(3);
        Console.WriteLine("bacon is ready");

        Toast toast = ToastBread(2);
        ApplyButter(toast);
        ApplyJam(toast);
        Console.WriteLine("toast is ready");

        Juice oj = PourOJ();
        Console.WriteLine("oj is ready");
        Console.WriteLine("Breakfast is ready!");
    }

    private static Juice PourOJ()
    {
        Console.WriteLine("Pouring orange juice");
        return new Juice();
    }

    private static void ApplyJam(Toast toast) =>
        Console.WriteLine("Putting jam on the toast");

    private static void ApplyButter(Toast toast) =>
        Console.WriteLine("Putting butter on the toast");

    private static Toast ToastBread(int slices)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        Console.WriteLine("Start toasting...");
        Task.Delay(3000).Wait();
        Console.WriteLine("Remove toast from toaster");

        return new Toast();
    }

    private static Bacon FryBacon(int slices)
    {
        Console.WriteLine($"putting {slices} slices of bacon in the pan");
        Console.WriteLine("cooking first side of bacon...");
        Task.Delay(3000).Wait();
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("flipping a slice of bacon");
        }
        Console.WriteLine("cooking the second side of bacon...");
        Task.Delay(3000).Wait();
        Console.WriteLine("Put bacon on plate");

        return new Bacon();
    }

    private static Egg FryEggs(int howMany)
    {
        Console.WriteLine("Warming the egg pan...");
        Task.Delay(3000).Wait();
        Console.WriteLine($"cracking {howMany} eggs");
        Console.WriteLine("cooking the eggs ...");
        Task.Delay(3000).Wait();
        Console.WriteLine("Put eggs on plate");

        return new Egg();
    }

    private static Coffee PourCoffee()
    {
        Console.WriteLine("Pouring coffee");
        return new Coffee();
    }
}

Asincrono :

public static async Task Main(string[] args)
    {
        Coffee cup = PourCoffee();
        Console.WriteLine("coffee is ready");

        Task<Toast> toastTask = ToastBreadAsync(2);
        Task<Egg> eggsTask = FryEggsAsync(2);
        Task<Bacon> baconTask = FryBaconAsync(3);
        Juice oj = PourOJ();
        Console.WriteLine("oj is ready");

        Toast toast = await toastTask;
        ApplyButter(toast);
        ApplyJam(toast);

        Egg eggs = await eggsTask;
        Bacon bacon = await baconTask;

        Console.WriteLine("Breakfast is ready!");
    }

    private static Juice PourOJ()
    {
        Console.WriteLine("Pouring orange juice");
        return new Juice();
    }

    private static void ApplyJam(Toast toast) =>
        Console.WriteLine("Putting jam on the toast");

    private static void ApplyButter(Toast toast) =>
        Console.WriteLine("Putting butter on the toast");

    private static async Task<Toast> ToastBreadAsync(int slices)
    {
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("Putting a slice of bread in the toaster");
        }
        Console.WriteLine("Start toasting...");
        await Task.Delay(3000);
        Console.WriteLine("Remove toast from toaster");

        return new Toast();
    }

    private static async Task<Bacon> FryBaconAsync(int slices)
    {
        Console.WriteLine($"putting {slices} slices of bacon in the pan");
        Console.WriteLine("cooking first side of bacon...");
        await Task.Delay(3000);
        for (int slice = 0; slice < slices; slice++)
        {
            Console.WriteLine("flipping a slice of bacon");
        }
        Console.WriteLine("cooking the second side of bacon...");
        await Task.Delay(3000);
        Console.WriteLine("Put bacon on plate");

        return new Bacon();
    }

    private static async Task<Egg> FryEggsAsync(int howMany)
    {
        Console.WriteLine("Warming the egg pan...");
        await Task.Delay(3000);
        Console.WriteLine($"cracking {howMany} eggs");
        Console.WriteLine("cooking the eggs ...");
        await Task.Delay(3000);
        Console.WriteLine("Put eggs on plate");

        return new Egg();
    }

    private static Coffee PourCoffee()
    {
        Console.WriteLine("Pouring coffee");
        return new Coffee();
    }
}

Consejos:

  1. Operaciones de E/S (Entrada/Salida): Siempre que tu función realice operaciones de E/S, como leer o escribir en un archivo, hacer solicitudes de red, interactuar con bases de datos o llamar a servicios web, deberías considerar hacerla asincrónica. Estas operaciones pueden tardar un tiempo significativo y bloquearían el hilo principal si se ejecutan de manera síncrona.

  2. Operaciones de larga duración: Si tu función realiza un trabajo que consume mucho tiempo (por ejemplo, procesamiento de imágenes, cálculos intensivos), y no quieres que tu aplicación se quede "congelada" o no responda durante este tiempo, deberías hacer esa función asincrónica.

  3. Interfaz de usuario fluida: En aplicaciones de escritorio, móviles o web, si una operación puede afectar la fluidez o la capacidad de respuesta de la interfaz de usuario, debería ejecutarse de manera asincrónica. Por ejemplo, cargar datos al iniciar una aplicación o realizar una consulta de búsqueda en una base de datos.

  4. Escalabilidad: En aplicaciones del lado del servidor, como las APIs web, usar asincronía ayuda a manejar un gran número de solicitudes concurrentes de manera más eficiente, ya que libera hilos para atender nuevas solicitudes mientras espera que las operaciones de E/S se completen.

  5. No para todo: No todas las funciones deben ser asincrónicas. Si una función realiza cálculos rápidos en memoria o tareas que se completan casi instantáneamente, no hay beneficio en hacerlas asincrónicas y puede incluso introducir una sobrecarga innecesaria.

  6. Cadenas de asincronía: Si una función llama a otra que es asincrónica, generalmente esa función también debería ser asincrónica. Esto evita que se bloqueen los hilos y ayuda a mantener la asincronía a lo largo de toda la cadena de llamadas.

Última actualización