Introducción a la programación asíncrona en C#
La programación asíncrona es un paradigma fundamental en el desarrollo moderno con C# (.NET), que permite crear aplicaciones receptivas y eficientes. A diferencia del enfoque síncrono, donde la ejecución del código bloquea el hilo actual hasta completar la operación, el código asíncrono libera el hilo para ejecutar otras tareas mientras se espera el resultado de una operación larga (por ejemplo, una consulta a la base de datos, una llamada HTTP o la lectura de un archivo).
Con la aparición de las palabras clave async y await en C# 5.0, la programación asíncrona dejó de ser compleja y se volvió accesible para todos los desarrolladores. En esta guía, analizaremos todos los conceptos clave: desde los fundamentos de Task y ValueTask hasta el manejo de errores y la cancelación de operaciones.
Fundamentos de async/await y Task
¿Qué son Task y Task<TResult>?
Task es el tipo central en TPL (Task Parallel Library), que representa una operación asíncrona. Task (sin valor de retorno) y Task<TResult> (con valor de retorno) son los bloques de construcción del código asíncrono.
// Método asíncrono simple sin valor de retornopublic async Task DoSomethingAsync(){ await Task.Delay(1000); Console.WriteLine("¡Listo!");}
// Método asíncrono con valor de retornopublic async Task<int> CalculateSumAsync(int a, int b){ await Task.Delay(500); // Simulación de una operación larga return a + b;}Palabras clave async y await
El modificador async indica al compilador que el método contiene código asíncrono. El operador await pausa la ejecución del método hasta que se complete la tarea esperada, sin bloquear el hilo.
public async Task ProcessDataAsync(){ Console.WriteLine("Inicio del procesamiento..."); // Espera asíncrona — el hilo no se bloquea string result = await FetchDataFromApiAsync(); Console.WriteLine($"Resultado obtenido: {result}");}Reglas de uso de async/await
- No use async void (excepto en controladores de eventos) — esto hace imposible el seguimiento de excepciones.
- Evite llamadas bloqueantes (
.Result,.Wait()) — pueden provocar deadlocks. - Nombre los métodos asíncronos con el sufijo "Async" para mayor claridad.
Técnicas avanzadas: ValueTask, CancellationToken y paralelismo
ValueTask vs Task
ValueTask es una optimización para casos donde la operación asíncrona a menudo se completa de forma síncrona (por ejemplo, datos en caché). A diferencia de Task, que es un tipo de referencia, ValueTask es una estructura, lo que reduce la carga en el recolector de basura.
public ValueTask<int> GetCachedOrFetchAsync(int id){ if (cache.ContainsKey(id)) { // Finalización síncrona — sin asignación de memoria return new ValueTask<int>(cache[id]); } // Finalización asíncrona return new ValueTask<int>(FetchFromDatabaseAsync(id));}Cancelación de operaciones con CancellationToken
CancellationToken permite interrumpir correctamente operaciones asíncronas largas. Siempre pase el token de cancelación si el método lo acepta.
public async Task<string> DownloadFileAsync(string url, CancellationToken ct){ using var client = new HttpClient(); var response = await client.GetAsync(url, ct); // ct se pasa a la API return await response.Content.ReadAsStringAsync();}
// Usovar cts = new CancellationTokenSource();cts.CancelAfter(TimeSpan.FromSeconds(5)); // Timeout de 5 segundos
try{ string data = await DownloadFileAsync("https://example.com", cts.Tok