Las Tasks son una forma de manejar de ejecutar tareas asíncrona en .NET. Permiten realizar operaciones en segundo plano sin bloquear el hilo principal, lo que resulta en una mejora significativa en la experiencia de usuario y en la eficiencia del programa.
Los Tasks son útiles, por ejemplo
- Operaciones de Entrada/Salida (I/O): como leer o escribir en un archivo, acceso a bases de datos, o realizar peticiones HTTP.
- Procesamiento intensivo: operaciones que consumen mucha CPU y que podrían ralentizar la interfaz de usuario en aplicaciones de escritorio o móviles.
- Ejecución paralela: cuando se necesita ejecutar múltiples tareas al mismo tiempo y esperar sus resultados.
¿Qué son los Tasks en C#?
Una Task es un objeto que representa una tarea que se está ejecutando o que está pendiente de ejecución. Esta tarea puede estar compuesta de varias operaciones, y pueden ser ejecutadas en paralelo con otras Tasks.
En C#, un Task es una clase (System.Threading.Tasks.Task
), y tiene un estado que indica su progreso y una vez que se completa, su resultado puede ser recuperado.
Creación y uso básico de Tasks
Un Task se puede crear de varias maneras en C#. Las formas más comunes son:
- Usando la clase
Task
directamente - Utilizando
async
yawait
Creación de un Task
Para crear un Task básico, puedes utilizar la clase Task
e iniciar una nueva tarea con Task.Run()
o Task.Factory.StartNew()
.
Por ejemplo, si se desea crear una Task para realizar una operación matemática, se puede escribir lo siguiente:
Task t = Task.Run(() => {
int result = 1 + 2;
Task.Delay(2000).Wait(); // Espera 2 segundos
Console.WriteLine("Resultado: " + result);
});
Aquí,
Task.Run
: Crea y ejecuta un Task que se ejecutará de forma asíncrona.Task.Delay
: Simula una tarea de espera (en este caso, 2 segundos) antes de terminar la ejecución del Task.
Esperar la finalización de una Task
Una vez que se ha creado una Task, puede ser necesario esperar a que se complete antes de continuar con otras operaciones.
Para hacer esto, se puede utilizar el método Wait() de la Task, que bloquea el hilo actual hasta que la Task se complete.
Task t = Task.Run(() => {
int result = 1 + 2;
Console.WriteLine("Resultado: " + result);
});
t.Wait();
Uso de async y await
Para simplificar el manejo de Tasks asíncronos, C# introduce las palabras clave async
y await
. El modificador async
se coloca en la declaración de un método, y await
permite esperar a que un Task termine sin bloquear el hilo principal. Aquí un ejemplo básico:
public static async Task Main()
{
Console.WriteLine("Inicio de la operación asíncrona...");
await OperacionAsincrona();
Console.WriteLine("Operación completada.");
}
public static async Task OperacionAsincrona()
{
await Task.Delay(2000); // Espera asíncrona de 2 segundos
Console.WriteLine("Dentro de la operación asíncrona.");
}
Lo vemos con más detalle en la siguiente entrada async/await
Manejo de excepciones en Tasks
El manejo de excepciones puede llegar a ser algo complicado. Las excepciones en los Tasks no se propagan inmediatamente al hilo principal; en su lugar, quedan almacenadas en el Task y deben capturarse usando bloques try-catch
junto con await
o task.Wait()
.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
try
{
await Task.Run(() => { throw new InvalidOperationException("Error en el Task"); });
}
catch (Exception ex)
{
Console.WriteLine($"Excepción capturada: {ex.Message}");
}
}
}
En este ejemplo, la excepción lanzada dentro del Task es capturada por el bloque try-catch
que envuelve el await
.
Ejecución de múltiples tasks
C# proporciona dos métodos muy útiles para trabajar con múltiples Tasks al mismo tiempo: Task.WhenAll
y Task.WhenAny
.
Task.WhenAll
Este método permite ejecutar varios Tasks de forma concurrente y espera hasta que todos se completen.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Task task1 = Task.Delay(2000);
Task task2 = Task.Delay(3000);
await Task.WhenAll(task1, task2); // Espera a que ambas tareas terminen
Console.WriteLine("Ambas tareas completadas.");
}
}
Task.WhenAny
Task.WhenAny
permite continuar la ejecución tan pronto como cualquiera de los Tasks haya terminado. Este método es útil cuando se necesita la respuesta más rápida de entre varias operaciones.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Task task1 = Task.Delay(2000);
Task task2 = Task.Delay(3000);
await Task.WhenAny(task1, task2); // Continúa cuando una tarea termine
Console.WriteLine("Al menos una tarea completada.");
}
}