🌀 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:
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.
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.
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.
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.
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.
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.