IRONSOFTWAREHOME

Dominando C# Async e Await

Dominando C# Async e Await

Tim Corey

38m 57s

A programação assíncrona pode parecer intimidante devido à sua terminologia complexa, mas oferece benefícios significativos, tornando os aplicativos mais rápidos e responsivos. O vídeo de Tim Corey sobre " C# Async/Await - Torne seu aplicativo mais responsivo e rápido com programação assíncrona " fornece um guia prático sobre como usar esses recursos de forma eficaz.

Neste artigo, usaremos o vídeo detalhado de Tim Corey para explicar esses conceitos, demonstrando as aplicações práticas de async e await por meio de exemplos claros. Abordaremos diversos cenários, desde manipuladores de eventos assíncronos até o gerenciamento de múltiplas tarefas, contextos de sincronização, tratamento de erros e as armadilhas comuns, como o uso de métodos assíncronos com a propriedade void. Ao final deste artigo, você terá um sólido entendimento de programação assíncrona e de como implementá-la em suas próprias aplicações C#.

Introdução

A programação assíncrona em C# é essencial para melhorar o desempenho das aplicações, especialmente ao trabalhar com operações que dependem muito de E/S ou de CPU. As palavras-chave async e await permitem que os desenvolvedores escrevam código assíncrono que pode executar tarefas de longa duração sem bloquear o thread principal. Ao marcar um método com a palavra-chave async, você sinaliza que o método realizará uma operação assíncrona, retornando um Task ou Task<t>. A palavra-chave await é usada dentro de um método assíncrono para pausar a execução até que a tarefa aguardada seja concluída, permitindo que outras operações sejam executadas simultaneamente.

Ao contrário da programação síncrona, em que as tarefas são executadas sequencialmente e podem bloquear a thread de chamada, os métodos assíncronos permitem lidar com várias tarefas simultaneamente, como processos em segundo plano ou processamento de entrada do usuário, sem congelar a thread da interface do usuário. Neste artigo, exploraremos como async e await funcionam juntos, cobrindo conceitos como métodos async void, tratamento de erros, trocas de contexto e contextos de sincronização. Você aprenderá como gerenciar com eficiência múltiplas operações assíncronas e lidar com exceções, garantindo uma execução tranquila em toda a thread de chamada, thread da interface do usuário e conclusão da tarefa. Essa abordagem é particularmente útil em cenários que envolvem sistemas de arquivos, solicitações de rede ou solicitações simultâneas, onde a capacidade de resposta é fundamental.

Tim introduz a programação assíncrona explicando a diferença entre operações síncronas e assíncronas. Na programação síncrona, as tarefas são executadas sequencialmente, causando atrasos se uma tarefa demorar muito. A programação assíncrona permite que as tarefas sejam executadas em paralelo ou em segundo plano, melhorando o desempenho e a capacidade de resposta.

Benefícios da Programação Assíncrona

Tim destaca dois benefícios principais da programação assíncrona:

  1. Melhoria na capacidade de resposta da interface do usuário (IU) : Ao executar tarefas de forma assíncrona, a IU permanece responsiva mesmo durante a execução de operações demoradas.

  2. Execução paralela : Tarefas independentes podem ser executadas em paralelo, reduzindo o tempo total necessário para concluí-las.

Demonstração do Aplicativo - Passo a Passo

Tim configura um aplicativo de demonstração do Windows Presentation Foundation (WPF) para demonstrar a diferença entre operações síncronas e assíncronas. O aplicativo possui dois botões: um para executar tarefas de forma síncrona e outro para executá-las de forma assíncrona.

Aplicação de Demonstração

O aplicativo também inclui um painel de resultados para exibir a saída.

Operação síncrona

Tim explica o código por trás da operação síncrona. Um Stopwatch é usado para medir o tempo de execução da tarefa.

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}";
}

Este código executa os seguintes passos:

  1. Inicie o cronômetro : mede o tempo de execução.
  2. Obter a lista de sites : Recupera uma lista de URLs de sites.
  3. Baixar cada site : Faz o download do conteúdo de cada site.
  4. Informações do site do relatório : Exibe o URL e o tamanho do conteúdo baixado.
  5. Parar o cronômetro : Interrompe o temporizador e exibe o tempo total de execução.

Observando a operação síncrona

Tim executa o aplicativo para demonstrar a operação síncrona. Ele observa que a interface do usuário deixa de responder enquanto os sites estão sendo baixados e que os resultados são exibidos todos de uma vez após a conclusão dos downloads.

Operação Síncrona

Para uma demonstração prática da forma imóvel, assista ao vídeo para melhor compreensão a partir de 9:20.

Criando uma tarefa assíncrona

Tim resolve o primeiro problema convertendo o método síncrono em um método assíncrono. Isso envolve o uso das palavras-chave async e await. Segue uma explicação passo a passo do processo:

  1. Copiar método síncrono existente : Tim copia o método síncrono existente e renomeia-o para indicar que ele será assíncrono.

    private async Task RunDownloadAsync()
    {
        // Same code as RunDownloadSync, but will be modified for async
    }
    C#
  2. Modificar a chamada de download para execução assíncrona :

    • Tim envolve a chamada de download em um Task.Run para executá-la assíncronamente.
    private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
    {
        return await Task.Run(() => DownloadWebsite(websiteURL));
    }
    C#
    • A palavra-chave await garante que o método espere que a tarefa assíncrona seja concluída antes de continuar.
  3. Certifique-se de que o método seja assíncrono :

    • A assinatura do método é atualizada para incluir a palavra-chave async, e ela retorna um Task.
    private async Task RunDownloadAsync()
    {
        List<string> websites = GetWebsiteList();
        foreach (var website in websites)
        {
            var result = await DownloadWebsiteAsync(website);
            ReportWebsiteInfo(website, result);
        }
    }
    C#
  4. Lide com o evento corretamente :

    • O manipulador de eventos de clique do botão foi atualizado para chamar o novo método assíncrono.
    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}";
    }
    C#

    Observe que os manipuladores de eventos podem retornar void mesmo que chamem métodos assíncronos.

Abordando a capacidade de resposta da interface do usuário

Tim mostra que, ao usar a palavra-chave await, a interface do usuário permanece responsiva. Isso permite que os usuários interajam com a janela enquanto a tarefa assíncrona está em execução.

// 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}";
}

Garantir a sincronização correta

Para garantir que o tempo total de execução seja relatado corretamente, Tim adiciona a palavra-chave await à chamada no manipulador de eventos de clique do botão.

// 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}";
}

Ao aguardar a conclusão do método assíncrono, o tempo de execução é medido com precisão e os resultados são exibidos à medida que são obtidos.

Criando processos assíncronos paralelos

Tim aborda a limitação de esperar que cada tarefa seja concluída sequencialmente, utilizando a execução paralela. Eis como ele modifica o código para conseguir isso:

  1. Copie o método assíncrono existente : Tim duplica o método assíncrono existente e o renomeia para indicar execução paralela.

    private async Task RunDownloadParallelAsync()
    {
        // Parallel execution logic will be added here
    }
    C#
  2. Criar uma lista de tarefas :

    • É criada uma lista de tarefas para armazenar todas as tarefas de download.
    List<Task<WebsiteDataModel>> tasks = new List<Task<WebsiteDataModel>>();
  3. Iniciar todas as tarefas sem esperar : Em vez de aguardar cada tarefa de download imediatamente, Tim as adiciona à lista de tarefas.

    foreach (var website in websites)
    {
        tasks.Add(DownloadWebsiteAsync(website));
    }
    C#
  4. Aguarde a conclusão de todas as tarefas :

    • O método Task.WhenAll é usado para aguardar a conclusão de todas as tarefas. Este método retorna uma matriz de resultados assim que todas as tarefas forem concluídas.
    WebsiteDataModel[] results = await Task.WhenAll(tasks);
  5. Processar os resultados : Após a conclusão de todas as tarefas, Tim processa os resultados em um loop.

    foreach (var result in results)
    {
        ReportWebsiteInfo(result);
    }
    C#

Segue o código completo para o método de execução paralela:

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);
    }
}

Atualizando o manipulador de eventos

Tim atualiza o manipulador de eventos de clique do botão para chamar o novo método de execução 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}";
}

Ao esperar por RunDownloadParallelAsync, o tempo total de execução é medido e relatado com precisão.

Observando a melhoria do desempenho

Tim demonstra a melhoria de desempenho executando o aplicativo em paralelo. Os resultados mostram uma redução significativa no tempo total de execução em comparação com a execução sequencial.

// Parallel execution
async Task RunDownloadParallelAsync();

O aumento de velocidade é evidente, pois os sites são baixados simultaneamente, reduzindo o tempo total de espera à duração do download individual mais lento.

Envolvendo Método em Task.Run() vs Chamada de Método Assíncrono

Tim explica o conceito de usar Task.Run() para envolver um método síncrono e torná-lo assíncrono quando você não pode modificar o método original. No entanto, ele também mostra a abordagem preferida de modificar o próprio método para ser assíncrono, caso você tenha controle sobre ele.

  1. Envolvendo Método em Task.Run(): Essa abordagem é útil quando você não pode alterar o código do método original, mas ainda deseja executá-lo de forma assíncrona.

    private async Task<WebsiteDataModel> DownloadWebsiteAsync(string websiteURL)
    {
        return await Task.Run(() => DownloadWebsite(websiteURL));
    }
    C#
  2. Tornando um método assíncrono :

    • Se for possível modificar o método, é melhor convertê-lo para assíncrono usando uma API assíncrona apropriada.

    • Tim demonstra isso alterando DownloadWebsite para DownloadWebsiteAsync e usando 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 };
    }
    C#
  3. Ajustando o método de chamada :

    • Após converter o método, o código de chamada precisa ser ajustado para remover o envoltório Task.Run() e chamar diretamente o método assíncrono.
    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);
        }
    }

Resumo dos pontos principais

Tim conclui com vários pontos importantes sobre o uso de async e await:

  • Garantir Métodos Retornando Tasks: Sempre que você marcar um método como async, ele deve retornar um Task ou Task<t> em vez de void (exceto para manipuladores de eventos).

  • Use await para Operações Confiáveis: Use a palavra-chave await quando você precisar do resultado de uma operação assíncrona antes de prosseguir.

  • Envolvimento de Tarefa para Código Não Modificável: Use Task.Run() para envolver métodos síncronos quando não puder modificar o método original.

  • Marcar Métodos Assíncronos Apropriadamente: Sempre adicione Async ao nome do método para indicar que é uma operação assíncrona.

  • Execução paralela versus sequencial : Decida se as tarefas podem ser executadas em paralelo ou se devem esperar umas pelas outras, com base nas dependências entre elas.

Tim enfatiza que a programação assíncrona em C# foi simplificada com async, await e Task. Complexidades como contextos de threading e modelos de apartamento são gerenciadas nos bastidores.

Conclusão

O tutorial de Tim Corey sobre async e await em C# oferece um recurso inestimável para dominar a programação assíncrona. Ao explicar cuidadosamente conceitos como paralelismo de tarefas, responsividade da interface do usuário e a importância de usar async e await no contexto correto, Tim fornece aos desenvolvedores as ferramentas para criar aplicativos mais rápidos e responsivos. Seu passo a passo detalhado demonstra como lidar de forma eficiente com tarefas de longa duração sem bloquear a thread principal, além de estratégias para gerenciar operações assíncronas e execução paralela.

Para uma compreensão mais aprofundada e exemplos práticos adicionais, recomendo vivamente que veja o vídeo de Tim Corey. Suas ideias sobre métodos assíncronos e vazios, tratamento de erros e gerenciamento de contextos de sincronização aprimorarão ainda mais sua capacidade de implementar esses conceitos em seus projetos.

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!

Let's Stay in Touch!

Join our newsletter, you’ll get exclusive access on article updates. We value your privacy

Key in blue circle

Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.

Your trial license will be sent to your email address

Sem limitações. 100% desbloqueado. Sem cartão de crédito.

OR
bullet_checkedNão é necessário cartão de crédito nem criação de conta.Sem limitações. 100% desbloqueado. Sem cartão de crédito.
  • Logo Aetna
  • Logo NASA
  • Logo GE
  • Logo Porsche
  • Logo USDA
  • Logo Qatar
Join Millions of Engineers who’ve tried Iron Suite
Agende sua consulta sem compromisso.
Preencha o formulário abaixo ou envie um e-mail para sales@ironsoftware.com
Os seus dados serão sempre mantidos em sigilo.
Aprovado por milhões de engenheiros em todo o mundo.
Logotipos dos clientes da Iron Software
Obtenha sua chave de avaliação gratuita de 30 dias instantaneamente.
Não é necessário cartão de crédito nem criação de conta.