Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

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

From 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.
214 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.

×