×
Namespaces

Variants
Actions

Como (não) paralisar a UI usando async-await

From Nokia Developer Wiki
Jump to: navigation, search

Este artigo explica possíveis causas de deadlocks (paralisações, travamentos) na UI de aplicações que usam async-await, e também discute maneiras de evitar que isso aconteça.

WP Metro Icon UI.png
SignpostIcon XAML 40.png
WP Metro Icon WP8.png
SignpostIcon WP7 70px.png
Article Metadata

Exemplo de código
Testado com
SDK: built and tested against Windows Phone 7.5 and Windows Phone 8 SDK

Compatibilidade
Artigo
Tradução:
Por paulo.morgado
Última alteração feita por lpvalente em 10 Aug 2013

Contents

Introdução

Com as novas funcionalidades da linguagem de programação C#' relacionadas a programação assíncrona, é possível escrever forma sequencial trechos de código que serão executados de forma assíncrona.

Quando um trecho de código aguarda que uma Task complete, sua execução permanecerá "semanticamente bloqueada" até que a Task se complete.

"Semanticamente bloqueado" quer dizer que o fluxo de execução do método chamador sera interrompida e retomada quando a operação assíncrona tiver sido completada. Isto normalmente quer dizer que o thread sera bloqueado. No entanto, em threads de UI (como é o caso da UI do Windows Phone) que necessitam que o sistema de mensagens (message pump) continue a trabalhar para manter a experiência de utilização com uma boa resposta, o fluxo de execução é retornado ao sistema de mensagens e quando a operação assíncrona terminar sera coiocada uma mensagem de continuação no sistema de mensagens e a execução é retomada na instrução seguinte.

A app de teste

A app de teste é composta apenas por um bloco de texto, uma barra de progresso (para mostrar atividade enquando o "trabalho" é feito na retaguarda) e dois botões (um que paralisará a UI e outro que não).

How to (not) deadlock your UI using async-await screenshot.png
<phone:PhoneApplicationPage
x:Class="PhoneApp.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
 
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
 
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="ASYNC-AWAIT BLOCKING" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="TextBlock1" TextWrapping="Wrap" VerticalAlignment="Top" Style="{StaticResource PhoneTextTitle1Style}" Text="00:00:00"/>
<ProgressBar x:Name="ProgressBar1" Height="10" IsIndeterminate="True" Visibility="Collapsed"/>
</StackPanel>
 
<StackPanel x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Button x:Name="Button1" Content="paralizar" Click="Button1_Click" />
<Button x:Name="Button2" Content="não paralizar" Click="Button2_Click" />
</StackPanel>
</Grid>
 
</phone:PhoneApplicationPage>

O método de teste

Para efeitos deste artigo, o nosso método assíncrono que faz "trabalho" na retaguarda apenas retornará a data/hora depois de um atraso de cinco segundos:

private async Task<DateTime> GetValueAsync()
{
await Task.Delay(5000);
return DateTime.Now;
}

Paralizando a UI

Quando se chamado um método destes a partir da UI podemos sentirmo-nos tentados a, em vez de aguardar que a Task complete, aceder diretamente à propriedade Result:

private void Butto1_Click(object sender, RoutedEventArgs e)
{
// ...
var datetime = GetValueAsync().Result;
this.TextBlock1.Text = datetime.ToLongTimeString();
// ...
}

Isto paralizará a UI porque o thread do sistema de mensagens será bloquado e a continuação que aí será colocada nunca será escalonada para ser executado, paralizando por isso a UI.

Não paralizando a UI

A forma mais simples para não paralizar a UI é aguardar quan a Task retornada complete em vez de aceder diretamente à propriedade Result:

private async void Button2_Click(object sender, RoutedEventArgs e)
{
// ...
var datetime = await GetValueAsync();
TextBlock1.Text = datetime.ToLongTimeString();
// ...
}

Desta forma, a UI continuará a tratar mensagens do sistema de mensagens e a continuação será escalonada para execução quando a tarefa terminar.

Conclusião

Quando se decide usar Tasks na UI deve-se sempre usar async-await porque o compilador de C# (e o compilador de Visual Basic) gerará uma máquina de estados que escalonará a continuação para ser executada no contexto correto sem paralizar a UI.

Projetos de exemplo

Descarregue uma solução com projetos de exemplo para Windows Phone 7.5 e Windows Phone 8 daqui: File:How to (not) deadlock your UI using async-await sample solution.zip

Recursos

This page was last modified on 10 August 2013, at 19:08.
64 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×