Breno Ferreira

Opniões e códigos de um desenvolvedor de software .NET.

Posts Tagged ‘Windows 8

Async no C#5

with one comment

Olá pessoal,

Hoje irei falar sobre um assunto bastante comentado nos últimos meses: assincronia. Assincronia está longe de ser um conceito novo, estando presente em várias linguagens a bastante tempo. No caso de linguagens C#, é possível escrever código assíncrono desde a primeira versão da linguagem.

Porque escrever código assíncrono

Operações assíncronas no nosso código são importantes quando desejamos executar alguma operação em background. Isso é de extrema importância quando precisamos ter uma interface gráfica responsiva, ou seja, que não fica bloqueada esperando alguma operação terminar. Quem nunca viu uma aplicação travar conforme a imagem abaixo?

image

Isso acontece por que a Thread responsável por tratar os comandos do usuário (geralmente chamada de UI Thread), está ocupada esperando alguma operação completar (no caso do Notepad acima, ela está bloqueada esperando um arquivo terminar de ser lido). Por isso, ela não pode executar nenhum comando do usuário. Para que isso não ocorra, devemos executar essas operações que levam um certo tempo para completar de maneira assíncrona, ou seja, em background, assim sendo possível deixar outras operações sejam executadas ao mesmo tempo.

Código Assíncrono no C#

Antes da versão 5 da linguagem, escrever código assíncrono era um tanto tedioso, e propício a erros. O desenvolvedor tinha que lidar com a interface IAsyncResult e então ter que lidar com callbacks. Abaixo está um exemplos de como isso era feito:

private void DownloadAsync(String uri)
{
	try
	{
		var webRequest = WebRequest.Create(uri);
		webRequest.BeginGetResponse(new AsyncCallback(HandleResponse), null);
	}
	catch(Exception)
	{
		Console.WriteLine("Error while downloading async");
	}
}

private void HandleResponse(IAsyncResult result)
{
	try
	{
		var response = webRequest.EndGetResponse(responseResult);
		StreamReader reader = new StreamReader(response.GetResponseStream());
		Console.WriteLine(reader.ReadToEnd());
	}
	catch(Exception)
	{
		Console.WriteLine("Error while handling download response");
	}
}

Repare que o código baseado em callbacks pode ficar meio confuso. O exemplo acima é bem simples, mas em casos mais complexos, com muitas chamadas assíncronas, o código tende a ficar bem complicado de ler, pois há “desvios” no código, e o programador é obrigado a desviar a leitura para outro trecho do código para ver a continuação da operação assíncrona. Se começarmos a adicionar IFs e Loops no código assíncrono, e ainda ter que fazer o tratamento de Exceptions, o código irá ficar bem complicado.

Async e Await

Com base nessas ocasionais dificuldades de se escrever código assíncrono, o time de desenvolvimento do C# criou uma nova técnica de se escrever código assíncrono no C#. Essa nova maneira de escrever código assíncrono no C# faz uso de duas novas keywords: async e await.

private async Task<String> DownloadAsync(String uri)
{
	try
	{
		var webRequest = WebRequest.Create(uri);
		var response = await webRequest.GetResponseAsync();

		StreamReader reader = new StreamReader(response.GetResponseStream());
		return reader.ReadToEnd();
	}
	catch(Exception)
	{
		Console.WriteLine("Error while downloading async");
	}
}

Bem mais simples, certo? O código ficou bem mais simples de entender, e ao mesmo tempo, está praticamente idêntico ao código síncrono, ou seja, sem callbacks e IAsyncResults espalhados pelo código.

Para dizermos que um método possui algum tipo de operação assíncrona, precisamos marcar o método como assíncrono, utilizando a keyword async antes do tipo de retorno do método. E quando desejamos receber o resultado de uma operação assíncrona, utilizamos a keyword await.

Importante: repare que a keyword await não faz com que a Thread bloqueie e literalmente espere o resultado da operação terminar. O que de fato acontece é que ao chegar no ponto onde há um await, o método retorna, e o que estiver abaixo (no exemplo acima, a instanciação da StreamReader e o retorno do resultado), é executado quando a operação assíncrona terminar, como se houvesse um callback mascarado.

Repare que ao invés de retornarmos uma String, retornamos uma Task<String>. Isso se deve ao fato de que o async do C# faz uso de Tasks para coordenar o fluxo de execução do código. Tasks nada mais são do que uma implementação de um conceito conhecido como Futures. Pense como se fosse uma representação de uma operação que ainda não completou, e que seu resultado ainda é desconhecido. Quando esta operação completar, quem tiver uma referência a esta Future (Task) irá ser notificado de que a operação terminou e poderá obter o resultado da operação. Então, no método acima, quando a execução chega em uma instrução que contém a keyword await, o método retorna uma Task representando a operação assíncrona que está sendo executada, neste caso, o download. Quando a operação terminar, quem estava em estado de await irá ser notificado e irá poder continuar com a execução do código.

Mais alguns exemplos

Operação assíncrona em um Loop:

public async Task<int> CountToTen()
{
	int count = 1;
	for (int i = 0; i < 10; i++)
	{
		await Task.Delay(500); //simulating async work
		count += i;
	}

	return count;
}

Neste exemplo estamos em um loop e executamos uma operação assíncrona no escopo deste loop. Como estamos usando a keyword await, a próxima iteração do loop só irá executar quando a operação assíncrona terminar.

Outra maneira de se fazer o await:

private static void PrintFromOneToThenThenFromElevenToTwentyUsingTasks()
{
	var task = 
		new Task(() =>
				Enumerable.Range(1, 10).ToList().ForEach(i =>
				{
					Thread.Sleep(10);
					Console.WriteLine(i);
				})
			 );

	task
		.GetAwaiter()
		.OnCompleted(() =>
					Enumerable.Range(11, 10).ToList().ForEach(i =>
					{
						Thread.Sleep(10);
						Console.WriteLine(i);
					})
				  );
	task.Start();

	task.Wait();
}

Neste exemplo, fazemos o await manualmente, ou seja, fazemos (grosseiramente) o que o compilador faz quando encontra a keyword await. O que acontece é que aqui pegamos um Awaiter e definimos um callback (action passada como parâmetro para o método OnCompleted) que será chamado quando a operação assíncrona terminar e pegamos o resultado (GetResult).

Conclusão

Vimos o quão fácil ficou escrever código assíncrono no C#5, ficando bastante parecido com código síncrono. Assim, conseguimos uma maior facilidade na hora de lidar com as complexidades que ocorrem quando a complexidade do nosso código aumenta.

Para saber mais, acesse a página do Async CTP na MSDN. Lá voce poderá baixar o Async CTP para o Visual Studio 2010, ou então baixar a versão CTP do Visual Studio 11 (recomendado!), baixar um Whitepaper, e assistir alguns vídeos do time do C# explicando em maiores detalhes. Publiquei também dois Gists (aqui e aqui) com alguns exemplos descritos aqui no post e mais alguns. Para mais exemplos, também recomendo acessar o 101 Async Examples

Abraços

Breno

PS: Gostaria de agradecer ao meu amigo Rodrigo Vidal por ter me ajudado e ter dado umas dicas para este post!

Advertisements

Written by Breno Ferreira

06/11/2011 at 20:46

Posted in C#

Tagged with , , ,

Overview do Windows Runtime (WinRT)

with one comment

Olá pessoal,

Hoje irei falar um pouco sobre a nova API de desenvolvimento de aplicações para o Windows 8, conhecida como Windows Runtime, ou simplesmente WinRT.

Por que uma nova API?

Tradicionalmente, quando desenvolvemos uma aplicação para o Windows (seja ele Windows 7, Vista, XP, ou anterior), quando precisamos acessar algum recurso do sistema operacional, como por exemplo, o sistema de arquivos, ou a recursos de rede, acessamos, de alguma maneira, uma API disponibilizada pelo sistema operacional. Essa API é conhecida como WinAPI, e é composta de milhares de funções e estruturas de dados. As funcionalidades da WinAPI são expostas em um modelo ao estilo de programação em C, ou seja, não é orientada a objetos. Isso a torna um tanto complexa, difícil de usar e aprender. Por essa razão, foram criadas algumas camadas de abstração em cima dessa API, sendo uma das mais famosa delas o .NET Framework. Muitas das APIs do .NET são wrappers, ou seja, encapsulam uma chamada a uma função da WinAPI para que o desenvolvedor não precise se preocupar em detalhes complexos da API nativa do Windows.

Windows Runtime (WinRT)

Para a construção de aplicações Metro do Windows 8, a Microsoft resolveu criar uma nova API chamada Windows Runtime, ou simplesmente WinRT. Essa API é completamente diferente da WinAPI tradicional, baseada no COM (Component Object Model), e toda a API possui metadados descrevendo cada componente, seja ele uma classe, interface, método, propriedade, etc., e tem também alguns recursos que oferecem maior segurança ao usuário.

image

Como você pode ver na figura acima, o Windows Runtime é utilizado para construção de aplicações Metro, que é o novo tipo de aplicação no Windows 8. Essas aplicações são imersivas e dão ao usuário uma experiência diferente daquela oferecida por aplicações desktop tradicionais.

Com o WinRT, é possível acessar vários recursos do sistema operacional, como arquivos, recursos de media e gráficos, recursos de rede, dados de geolocation, sensores, entre outros recursos. E para criar aplicações que utilizam os componentes do WinRT, o desenvolvedor poderá escolher sua linguagem de preferência, seja ela C++, C# ou VB.NET, ou Javascript. E para a construção da interface de usuário (UI), caso o desenvolvedor escreva a aplicação em C#, VB ou C++, ele irá utilizar XAML para definição dos elementos da UI. No caso do Javascript, será usado HTML5+CSS3.

Arquitetura do Windows Runtime

image

O WinRT é, obviamente, uma API implementada em código nativo, e é utilizada amplamente pelo sistema operacional para executar suas aplicações, e pela aplicação em si para interagir com o usuário e o sistema operacional.

Cada tipo de dados no WinRT possui metadados sobre ele mesmo, e esses metadados são criados seguindo o mesmo padrão dos metadados do .NET Framework. Esses arquivos de metadados estão localizados no seguinte diretório: C:\Windows\System32\WinMetadata. Nesse diretório, há vários arquivos .winmd, que são os arquivos contendo os metadados sobre os tipos de dados do WinRT. A imagem abaixo mostra o arquivo “Windows.Storage.winmd” aberto no IL Disassembler.

image

Podemos ver que ele mostra informações sobre os métodos, propriedades, etc.. sobre os tipos contidos na biblioteca de Storage do WinRT. Repare que não há Intermediate Language gerada. Isso acontece por que a implementação dos métodos é nativa, e está contida em algumas dlls do sistema operacional. Esses arquivos .winmd contém somente metadados.

Como o WinRT é baseado em COM, todos os tipos de dados expostos por ele são baseados em Interfaces. E quando ativamos um objeto do WinRT, estamos, na verdade, recebendo uma referência de um objeto que implementa os métodos desta interface.

As linguagens que utilizamos para acessar o WinRT são por sua vez, baseadas em classes, e cada uma tem suas particularidades de utilização de objetos. É exatamente para prover esse mapeamento, e acesso às interfaces do WinRT pelas linguagens utilizadas para desenvolver os aplicativos (C++, C#, VB e JS), e prover uma experiência de desenvolvimento familiar ao desenvolvedor, que a foi criado um componente chamado Language Projection. Assim, o desenvolvedor fica livre de ter que lidar com particularidades de APIs baseadas em COM e trabalha com os objetos da mesma maneira que ele já está acostumado na sua linguagem de preferência.

Outro componente muito importante do WinRT, é o Runtime Broker. Certas APIs do WinRT expoem dados privados do usuário, como arquivos nas pastas pessoais, dados da webcam, dados de geolocation, entre outros. Uma aplicação construída sobre o WInRT não tem acesso direto a esses dados. Quando uma aplicação chama um método para ter acesso a esses dados, a execução da aplicação é desviada para um processo chamado RuntimeBroker.exe que irá verificar se sua aplicação tem privilégios para acessar esses dados, e irá também mostrar um prompt para o usuário perguntando se ele permite que a aplicação acesse estes dados. Se o usuário permitir, então sua aplicação irá poder acessar os dados requisitados. Além disso, para acessar esses recursos, a aplicação terá que definir em um arquivo de manifesto, quais recursos ela irá acessar.

Por último, se uma aplicação é criada em C#/VB ou Javascript, essa aplicação rodará sobre um ambiente de execução, como tradicionalmente aplicações construídas com essas linguagens são executadas. No caso do C#/VB, as aplicações irão rodar sobre a CLR (Common Language Runtime), e aplicações feitas com HTML e Javascript irão rodar sobre a engine do Internet Explorer 10.

Assincronia

Como o Windows 8 é um sistema operacional projetado para rodar em dispositivos móveis como tablets e slate PCs, a Microsoft se preocupou muito em ter certeza que a User Experience fosse a mais responsiva e rápida possível. Por isso, uma das decisões feitas no design da WinRT foi que qualquer chamada a método que pudesse demorar mais que 50 milisegundos, iria ser uma chamada assíncrona. Isso significa que todas as operações de IO, ações que dependem de alguma ação do usuário, e algumas outras, serão assíncronas.

Por hoje é só. Em breve, irei postar mais algumas coisas aqui mais detalhadamente sobre outros assuntos relacionados ao Windows Runtime. Até lá.

Breno Ferreira

Written by Breno Ferreira

23/10/2011 at 14:41

Posted in Windows 8

Tagged with ,

%d bloggers like this: