Visão geral do suporte assíncrono

O C# 5 introduziu duas palavras-chave para simplificar a programação assíncrona: async e await. Essas palavras-chave permitem escrever código simples que utiliza a Biblioteca Paralela de Tarefas para executar operações de longa execução (como acesso à rede) em outro thread e acessar facilmente os resultados na conclusão. As versões mais recentes do Xamarin.iOS e Xamarin.Android suportam assíncrono e aguardam - este documento fornece explicações e um exemplo de uso da nova sintaxe com o Xamarin.

O suporte assíncrono do Xamarin é criado na base do Mono 3.0 e atualiza o perfil da API de ser uma versão compatível com dispositivos móveis do Silverlight para ser uma versão compatível com dispositivos móveis do .NET 4.5.

Visão geral

Este documento apresenta as novas palavras-chave async e await e, em seguida, apresenta alguns exemplos simples de implementação de métodos assíncronos no Xamarin.iOS e no Xamarin.Android.

Para obter uma discussão mais completa dos novos recursos assíncronos do C# 5 (incluindo muitos exemplos e diferentes cenários de uso), consulte o artigo Programação assíncrona.

O aplicativo de exemplo faz uma solicitação da Web assíncrona simples (sem bloquear o thread principal) e, em seguida, atualiza a interface do usuário com o html baixado e a contagem de caracteres.

The sample application makes a simple asynchronous web request without blocking the main thread then updates the UI with the downloaded html and character count

O suporte assíncrono do Xamarin é criado na base do Mono 3.0 e atualiza o perfil da API de ser uma versão compatível com dispositivos móveis do Silverlight para ser uma versão compatível com dispositivos móveis do .NET 4.5.

Requisitos

Os recursos do C# 5 exigem o Mono 3.0 incluído no Xamarin.iOS 6.4 e no Xamarin.Android 4.8. Você será solicitado a atualizar seu Mono, Xamarin.iOS, Xamarin.Android e Xamarin.Mac para aproveitá-lo.

Usando async & await

async e await são novos recursos de linguagem C# que funcionam em conjunto com a Biblioteca Paralela de Tarefas para facilitar a escrita de código encadeado para executar tarefas de longa execução sem bloquear o thread principal do seu aplicativo.

async

Declaração

A async palavra-chave é colocada em uma declaração de método (ou em um método lambda ou anônimo) para indicar que ela contém código que pode ser executado de forma assíncrona, ou seja, não bloquear o thread do chamador.

Um método marcado com async deve conter pelo menos uma expressão ou instrução await. Se nenhuma await instrução estiver presente no método, ele será executado de forma síncrona (o mesmo que se não houvesse modificador async ). Isso também resultará em um aviso do compilador (mas não em um erro).

Tipos de retorno

Um método assíncrono deve retornar um Task, Task<TResult> ou void.

Especifique o tipo de retorno se o Task método não retornar nenhum outro valor.

Especifique Task<TResult> se o método precisa retornar um valor, onde TResult é o tipo que está sendo retornado (como um int, por exemplo).

O void tipo de retorno é usado principalmente para manipuladores de eventos que o exigem. O código que chama métodos assíncronos de retorno de vazio não await pode no resultado.

Parâmetros

Métodos assíncronos não podem declarar ref ou out parâmetros.

await

O operador await pode ser aplicado a uma Task dentro de um método marcado como async. Isso faz com que o método pare a execução nesse ponto e aguarde até que a tarefa seja concluída.

O uso de await não bloqueia o thread do chamador – em vez disso, o controle é retornado ao chamador. Isso significa que o thread de chamada não está bloqueado, portanto, por exemplo, o thread da interface do usuário não seria bloqueado ao aguardar uma tarefa.

Quando a tarefa é concluída, o método continua a execução no mesmo ponto no código. Isso inclui retornar ao escopo try de um bloco try-catch-finally (se houver um). await não pode ser usado em um bloqueio de captura ou finalmente.

Leia mais sobre aguarde.

Tratamento de exceção

As exceções que ocorrem dentro de um método assíncrono são armazenadas na tarefa e lançadas quando a tarefa é awaited. Essas exceções podem ser capturadas e manipuladas dentro de um bloco try-catch.

Cancelamento

Os métodos assíncronos que levam muito tempo para serem concluídos devem oferecer suporte ao cancelamento. Normalmente, o cancelamento é invocado da seguinte maneira:

  • Um CancellationTokenSource objeto é criado.
  • A CancellationTokenSource.Token instância é passada para um método assíncrono cancelável.
  • O cancelamento é solicitado ligando para o CancellationTokenSource.Cancel método.

A tarefa então se cancela e reconhece o cancelamento.

Para obter mais informações sobre o cancelamento, consulte Ajuste fino de seu aplicativo assíncrono (C#).

Exemplo

Baixe o exemplo de solução Xamarin (para iOS e Android) para ver um exemplo funcional de async e await em aplicativos móveis. O código de exemplo é discutido em mais detalhes nesta seção.

Escrevendo um método assíncrono

O método a seguir demonstra como codificar um async método com uma awaittarefa ed:

public async Task<int> DownloadHomepage()
{
    var httpClient = new HttpClient(); // Xamarin supports HttpClient!

    Task<string> contentsTask = httpClient.GetStringAsync("https://visualstudio.microsoft.com/xamarin"); // async method!

    // await! control returns to the caller and the task continues to run on another thread
    string contents = await contentsTask;

    ResultEditText.Text += "DownloadHomepage method continues after async call. . . . .\n";

    // After contentTask completes, you can calculate the length of the string.
    int exampleInt = contents.Length;

    ResultEditText.Text += "Downloaded the html and found out the length.\n\n\n";

    ResultEditText.Text += contents; // just dump the entire HTML

    return exampleInt; // Task<TResult> returns an object of type TResult, in this case int
}

Observe estes pontos:

  • A declaração de método inclui a async palavra-chave.
  • O tipo de retorno é Task<int> para que o código de chamada possa acessar o int valor calculado nesse método.
  • A instrução return é que é return exampleInt; um objeto inteiro – o fato de que o método retorna Task<int> faz parte das melhorias de linguagem.

Chamando um método assíncrono 1

Esse manipulador de eventos de clique no botão pode ser encontrado no aplicativo de exemplo do Android para chamar o método discutido acima:

GetButton.Click += async (sender, e) => {

    Task<int> sizeTask = DownloadHomepage();

    ResultTextView.Text = "loading...";
    ResultEditText.Text = "loading...\n";

    // await! control returns to the caller
    var intResult = await sizeTask;

    // when the Task<int> returns, the value is available and we can display on the UI
    ResultTextView.Text = "Length: " + intResult ;
    // "returns" void, since it's an event handler
};

Observações:

  • O delegado anônimo tem o prefixo de palavra-chave assíncrona.
  • O método assíncrono DownloadHomepage retorna um int Task que é armazenado na variável sizeTask<>.
  • O código aguarda na variável sizeTask. Esse é o local em que o método é suspenso e o controle é retornado ao código de chamada até que a tarefa assíncrona seja concluída em seu próprio thread.
  • A execução não pausa quando a tarefa é criada na primeira linha do método, apesar da tarefa ser criada lá. A palavra-chave await significa o local onde a execução está pausada.
  • Quando a tarefa assíncrona é concluída, intResult é definido e a execução continua no thread original, a partir da linha de espera.

Chamando um método assíncrono 2

No aplicativo de exemplo do iOS, o exemplo é escrito de forma ligeiramente diferente para demonstrar uma abordagem alternativa. Em vez de usar um representante anônimo, este exemplo declara um manipulador de eventos atribuído como um async manipulador de eventos regular:

GetButton.TouchUpInside += HandleTouchUpInside;

O método do manipulador de eventos é definido conforme mostrado aqui:

async void HandleTouchUpInside (object sender, EventArgs e)
{
    ResultLabel.Text = "loading...";
    ResultTextView.Text = "loading...\n";

    // await! control returns to the caller
    var intResult = await DownloadHomepage();

    // when the Task<int> returns, the value is available and we can display on the UI
    ResultLabel.Text = "Length: " + intResult ;
}

Alguns pontos importantes:

  • O método é marcado como async mas retorna void . Isso normalmente é feito apenas para manipuladores de eventos (caso contrário, você retornaria um Task ou Task<TResult> ).
  • A await palavra-chave no DownloadHomepage método atribui diretamente a uma variável (intResult), ao contrário do exemplo anterior, onde usamos uma variável intermediária Task<int> para fazer referência à tarefa. Esse é o local onde o controle é retornado ao chamador até que o método assíncrono seja concluído em outro thread.
  • Quando o método assíncrono é concluído e retornado, a await execução é retomada no que significa que o resultado inteiro é retornado e, em seguida, renderizado em um widget de interface do usuário.

Resumo

Usar async e await simplifica muito o código necessário para gerar operações de longa execução em threads em segundo plano sem bloquear o thread principal. Eles também facilitam o acesso aos resultados quando a tarefa é concluída.

Este documento forneceu uma visão geral das novas palavras-chave e exemplos de linguagem para Xamarin.iOS e Xamarin.Android.