Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
38m 57s
La programación asíncrona puede parecer desalentadora por su compleja terminología, pero ofrece importantes ventajas a la hora de agilizar y mejorar la capacidad de respuesta de las aplicaciones. El vídeo de Tim Corey sobre "C# Async / Await - Make your app more responsive and faster with asynchronous programming" ofrece una guía práctica sobre el uso eficaz de estas funciones.
En este artículo, utilizaremos el detallado vídeo de Tim Corey para desglosar estos conceptos, demostrando las aplicaciones prácticas de async y await mediante ejemplos claros. Cubriremos varios escenarios, desde manejadores de eventos asíncronos hasta el manejo de múltiples tareas, contextos de sincronización, manejo de errores y los errores más comunes como el uso de métodos async void. Al final de este artículo, tendrás una sólida comprensión de la programación asíncrona y de cómo implementarla en tus propias aplicaciones de C#.
La programación asíncrona en C# es esencial para mejorar el rendimiento de las aplicaciones, especialmente cuando se trabaja con operaciones ligadas a la E/S o a la CPU. Las palabras clave async y await permiten a los desarrolladores escribir código asincrónico que puede ejecutar tareas de larga duración sin bloquear el hilo principal. Al marcar un método con la palabra clave async, se indica que el método realizará una operación asincrónica, devolviendo un Task o Task<t>. La palabra clave await se utiliza dentro de un método async para pausar la ejecución hasta que la tarea en espera se complete, permitiendo que otras operaciones se ejecuten simultáneamente.
A diferencia de la programación síncrona, en la que las tareas se ejecutan secuencialmente y pueden bloquear el hilo de llamada, los métodos asíncronos permiten gestionar varias tareas simultáneamente, como procesos en segundo plano o la gestión de la entrada del usuario, sin congelar el hilo de la interfaz de usuario. En este artículo, vamos a explorar cómo async y await funcionan juntos, cubriendo conceptos como métodos async void, manejo de errores, cambios de contexto y contextos de sincronización. Aprenderá a gestionar eficazmente múltiples operaciones asíncronas y a manejar excepciones, garantizando una ejecución fluida entre el subproceso de llamada, el subproceso de interfaz de usuario y la finalización de la tarea. Este enfoque es especialmente útil para situaciones relacionadas con sistemas de archivos, solicitudes de red o solicitudes simultáneas en las que la capacidad de respuesta es clave.
Tim introduce la programación asíncrona explicando la diferencia entre operaciones síncronas y asíncronas. En la programación síncrona, las tareas se realizan de forma secuencial, lo que provoca retrasos si una tarea lleva mucho tiempo. La programación asíncrona permite que las tareas se ejecuten en paralelo o en segundo plano, lo que mejora el rendimiento y la capacidad de respuesta.
Tim destaca dos ventajas principales de la programación asíncrona:
Mejora de la capacidad de respuesta de la interfaz de usuario: al realizar tareas de forma asíncrona, la interfaz de usuario mantiene su capacidad de respuesta incluso cuando se ejecutan operaciones de larga duración.
Ejecución en paralelo: las tareas independientes pueden ejecutarse en paralelo, lo que reduce el tiempo total necesario para completarlas.
Tim crea una aplicación de demostración de Windows Presentation Foundation (WPF) para mostrar la diferencia entre operaciones síncronas y asíncronas. La aplicación tiene dos botones: uno para ejecutar tareas de forma sincrónica y otro para ejecutarlas de forma asincrónica.

La aplicación también incluye un panel de resultados para mostrar el resultado.
Tim explica el código que hay detrás del funcionamiento síncrono. Un Stopwatch se utiliza para medir el tiempo de ejecución de la tarea.
private void ExecuteSync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
List<string> websites = GetWebsiteList();
foreach (var website in websites)
{
string result = DownloadWebsite(website);
ReportWebsiteInfo(website, result);
}
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}
private List<string> GetWebsiteList()
{
return new List<string>
{
"https://www.yahoo.com",
"https://www.google.com",
"https://www.microsoft.com",
"https://www.cnn.com",
"https://www.codeproject.com",
"https://www.stackoverflow.com"
};
}
private string DownloadWebsite(string websiteURL)
{
WebClient client = new WebClient();
return client.DownloadString(websiteURL);
}
private void ReportWebsiteInfo(string website, string data)
{
ResultsWindow.Text += $"{website}: {data.Length} characters{Environment.NewLine}";
}Private Sub ExecuteSync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Dim websites As List(Of String) = GetWebsiteList()
For Each website In websites
Dim result As String = DownloadWebsite(website)
ReportWebsiteInfo(website, result)
Next
stopwatch.Stop()
ResultsWindow.Text &= $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End Sub
Private Function GetWebsiteList() As List(Of String)
Return New List(Of String) From {
"https://www.yahoo.com",
"https://www.google.com",
"https://www.microsoft.com",
"https://www.cnn.com",
"https://www.codeproject.com",
"https://www.stackoverflow.com"
}
End Function
Private Function DownloadWebsite(websiteURL As String) As String
Dim client As New WebClient()
Return client.DownloadString(websiteURL)
End Function
Private Sub ReportWebsiteInfo(website As String, data As String)
ResultsWindow.Text &= $"{website}: {data.Length} characters{Environment.NewLine}"
End SubEste código realiza los siguientes pasos:
Tim ejecuta la aplicación para demostrar el funcionamiento síncrono. Señala que la interfaz de usuario deja de responder mientras se descargan los sitios web y que los resultados se muestran todos a la vez una vez finalizadas las descargas.

Para una demostración práctica de que el formulario no se mueve, vea el vídeo para una mejor comprensión en el minuto 9:20.
Tim aborda el primer problema convirtiendo el método síncrono en asíncrono. Esto implica el uso de las palabras clave async y await. A continuación se explica el proceso paso a paso:
Copiar método sincrónico existente:
private async Task RunDownloadAsync()
{
// Same code as RunDownloadSync, but will be modified for async
}
Modificar llamada de descarga para ejecución asíncrona:
Task.Run para ejecutarla de manera asincrónica.private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
return await Task.Run(() => DownloadWebsite(websiteURL));
}
await asegura que el método espere a que la tarea asincrónica se complete antes de continuar.Asegúrate de que el método es asíncrono:
async, y devuelve un Task.private async Task RunDownloadAsync()
{
List<string> websites = GetWebsiteList();
foreach (var website in websites)
{
var result = await DownloadWebsiteAsync(website);
ReportWebsiteInfo(website, result);
}
}
Manejar el evento correctamente:
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}
Tim muestra que al usar la palabra clave await, la interfaz de usuario permanece receptiva. Esto permite a los usuarios interactuar con la ventana mientras la tarea asincrónica se está ejecutando.
// UI remains responsive
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}' UI remains responsive
Private Async Sub ExecuteAsync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Await RunDownloadAsync()
stopwatch.Stop()
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End SubPara asegurar que el tiempo total de ejecución se informe correctamente, Tim añade la palabra clave await a la llamada en el controlador de eventos de clic del botón.
// Correctly waits for the asynchronous task to complete
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}' Correctly waits for the asynchronous task to complete
Private Async Sub ExecuteAsync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Await RunDownloadAsync()
stopwatch.Stop()
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End SubAl esperar a que se complete el método asíncrono, el tiempo de ejecución se mide con precisión y los resultados se muestran a medida que se recuperan.
Tim aborda la limitación de esperar a que cada tarea se complete secuencialmente mediante el uso de la ejecución en paralelo. He aquí cómo modifica el código para conseguirlo:
Copiar el método asíncrono existente:
private async Task RunDownloadParallelAsync()
{
// Parallel execution logic will be added here
}
Crear una lista de tareas:
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();Dim tasks As New List(Of Task(Of WebsiteDataModel))()Inicia todas las tareas sin esperar:
foreach (var website in websites)
{
tasks.Add(DownloadWebsiteAsync(website));
}
Espere a que se completen todas las tareas:
Task.WhenAll se utiliza para esperar a la culminación de todas las tareas. Este método devuelve una matriz de resultados una vez completadas todas las tareas.WebsiteDataModel[] results = await Task.WhenAll(tasks);Dim results As WebsiteDataModel() = Await Task.WhenAll(tasks)Procesamiento de los resultados:
foreach (var result in results)
{
ReportWebsiteInfo(result);
}
Aquí está el código completo para el método de ejecución en paralelo:
private async Task RunDownloadParallelAsync()
{
List<string> websites = GetWebsiteList();
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();
foreach (var website in websites)
{
tasks.Add(DownloadWebsiteAsync(website));
}
WebsiteDataModel[] results = await Task.WhenAll(tasks);
foreach (var result in results)
{
ReportWebsiteInfo(result);
}
}Private Async Function RunDownloadParallelAsync() As Task
Dim websites As List(Of String) = GetWebsiteList()
Dim tasks As New List(Of Task(Of WebsiteDataModel))()
For Each website As String In websites
tasks.Add(DownloadWebsiteAsync(website))
Next
Dim results As WebsiteDataModel() = Await Task.WhenAll(tasks)
For Each result As WebsiteDataModel In results
ReportWebsiteInfo(result)
Next
End FunctionTim actualiza el controlador de eventos de clic del botón para llamar al nuevo método de ejecución paralela.
private async void ExecuteAsync_Click(object sender, RoutedEventArgs e)
{
Stopwatch stopwatch = Stopwatch.StartNew();
await RunDownloadParallelAsync();
stopwatch.Stop();
ResultsWindow.Text += $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}";
}Private Async Sub ExecuteAsync_Click(sender As Object, e As RoutedEventArgs)
Dim stopwatch As Stopwatch = Stopwatch.StartNew()
Await RunDownloadParallelAsync()
stopwatch.Stop()
ResultsWindow.Text &= $"Total execution time: {stopwatch.ElapsedMilliseconds} ms{Environment.NewLine}"
End SubAl esperar RunDownloadParallelAsync, se mide e informa con precisión el tiempo total de ejecución.
Tim demuestra la mejora del rendimiento ejecutando la aplicación en paralelo. Los resultados muestran una reducción significativa del tiempo total de ejecución en comparación con la ejecución secuencial.
// Parallel execution
async Task RunDownloadParallelAsync();' Parallel execution
Async Function RunDownloadParallelAsync() As Task
End FunctionEl aumento de velocidad es evidente a medida que los sitios web se descargan simultáneamente, reduciendo el tiempo total de espera a la duración de la descarga individual más lenta.
Task.Run() vs Llamada a Método AsincrónicoTim explica el concepto de usar Task.Run() para envolver un método sincrónico y convertirlo en asincrónico cuando no se puede modificar el método original. Sin embargo, también muestra el enfoque preferido de modificar el propio método para que sea asíncrono si se tiene control sobre él.
Envolviendo Método en Task.Run():
private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
return await Task.Run(() => DownloadWebsite(websiteURL));
}
Cómo hacer que un método sea asíncrono:
Si puede modificar el método, es mejor convertir el propio método para que sea asíncrono utilizando una API asíncrona adecuada.
Tim demuestra esto cambiando DownloadWebsite a DownloadWebsiteAsync y utilizando DownloadStringTaskAsync de WebClient.
private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
{
WebClient client = new WebClient();
string data = await client.DownloadStringTaskAsync(websiteURL);
return new WebsiteDataModel { URL = websiteURL, Data = data };
}
Ajuste del método de llamada:
Task.Run() y llamar directamente al método asincrónico.private async Task RunDownloadParallelAsync()
{
List<string> websites = GetWebsiteList();
List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();
foreach (var website in websites)
{
tasks.Add(DownloadWebsiteAsync(website));
}
WebsiteDataModel[] results = await Task.WhenAll(tasks);
foreach (var result in results)
{
ReportWebsiteInfo(result);
}
}Private Async Function RunDownloadParallelAsync() As Task
Dim websites As List(Of String) = GetWebsiteList()
Dim tasks As New List(Of Task(Of WebsiteDataModel))()
For Each website As String In websites
tasks.Add(DownloadWebsiteAsync(website))
Next
Dim results As WebsiteDataModel() = Await Task.WhenAll(tasks)
For Each result As WebsiteDataModel In results
ReportWebsiteInfo(result)
Next
End FunctionTim concluye con varios consejos importantes sobre el uso de async y await:
Asegurar Métodos que Devuelven Tareas: Siempre que marques un método como async, debe devolver un Task o Task<t> en lugar de void (excepto para los manejadores de eventos).
Usar await para Operaciones Confiables: Usa la palabra clave await cuando necesitas el resultado de una operación asincrónica antes de continuar.
Envoltura de Tarea para Código No Modificable: Usa Task.Run() para envolver métodos sincrónicos cuando no puedes modificar el método original.
Marcar Métodos Asíncronos Adecuadamente: Siempre añade Async al nombre del método para indicar que es una operación asincrónica.
Ejecución en paralelo frente a ejecución secuencial: Decidir si las tareas pueden ejecutarse en paralelo o deben esperarse unas a otras en función de las dependencias entre tareas.
Tim enfatiza que la programación asincrónica en C# se ha simplificado con async, await, y Task. Las complejidades como los contextos de hilos y los modelos de apartamentos se gestionan entre bastidores.
El tutorial de Tim Corey sobre async y await en C# ofrece un recurso inestimable para dominar la programación asíncrona. Al explicar cuidadosamente conceptos como el paralelismo de tareas, la capacidad de respuesta de la interfaz de usuario y la importancia de utilizar async y await en el contexto adecuado, Tim proporciona a los desarrolladores las herramientas necesarias para crear aplicaciones más rápidas y con mayor capacidad de respuesta. Su detallado recorrido demuestra cómo gestionar eficazmente las tareas de larga duración sin bloquear el hilo principal, así como las estrategias para gestionar las operaciones asíncronas y la ejecución en paralelo.
Para una comprensión más profunda y ejemplos prácticos adicionales, recomiendo encarecidamente ver el vídeo de Tim Corey. Sus conocimientos sobre los métodos async void, el tratamiento de errores y la gestión de contextos de sincronización mejorarán aún más su capacidad para implementar estos conceptos en sus proyectos.
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy