Porque é
importante?
Como todos sabem, Programação Assíncrona é muito
importante porque tem como objetivo principalmente aperfeiçoar a capacidade de
resposta de nossas aplicações. O .NET sempre tem tentando apoiar esta forma
desde sua primeira versão e sempre houveram novos recursos em cada release e
apresentações de novas maneiras de assincronismo. Todas estas funcionalidades
estão fora do escopo deste artigo, mas aqui estão vários recursos falando com
detalhes sobre eles e descrevendo o seu uso com detalhes.
No entanto, nós desenvolvedores costumamos escrever métodos,
eu digo métodos síncronos e tentamos chama-los assincronamente, de várias
maneiras (Thread, ThreadStart, ThreadPool, BackgroundWorker, etc), mas a
escrita de métodos assíncronos naturalmente foi difícil de fazer e manter.
O recurso pode ser resumido pelo seguinte exemplo: nós
mudaremos o método para assíncrono naturalmente apenas alterando o seu tipo de
retorno.
public
static IEnumerable<int> getPrimes(int min, int count)
{
return Enumerable.Range(min, count).Where
(n => Enumerable.Range(2,
(int)Math.Sqrt(n) - 1).All(i =>
n % i > 0));
}
Dependendo dos parâmetros min e count esse método
pode durar um bom tempo. Uma maneira de torna-lo assíncrono é simplesmente trocando o
tipo de retorno como no exemplo a seguir:
public static Task<IEnumerable<int>> getPrimesAsync(int min, int count)
{
return Task.Run (()=> Enumerable.Range(min, count).Where
(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i =>
n % i > 0)));
}
No exemplo acima, notem que nós mudamos o nome do próprio
método adicionando Async como prefixo e essa é a regra que devemos seguir. Como vocês podem ver o retorno é uma Task, anteriormente era
void e foi mudado para Task<T> onde T é o tipo de retorno do método.
O que é Task
?
Agora quando um método retorna Task<T> ele fica no
estado awaitable isso significa que
você pode chama-lo usando a keyword await,
e sempre que você usar await o controle da execução voltará imediatamente e não
haverá impacto na capacidade de resposta da sua aplicação.
Vamos ver como nós chamamos esses métodos acima:
private static void PrintPrimaryNumbers()
{
for (int i = 0; i < 10; i++)
getPrimes(i * 100000 + 1, i * 1000000)
.ToList().
ForEach(x => Trace.WriteLine(x));
}
PrintPrimaryNumbers()
é um método que podemos chamar diretamente e tradicionalmente, neste exemplo
estou chamando o método 10 vezes, ele será chamado consequentemente e nós
veremos quanto tempo demora até ele ser finalizado.
private static async void PrintPrimaryNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
var result = await getPrimesAsync(i * 100000 + 1, i * 1000000);
result.ToList().ForEach(x => Trace.WriteLine(x));
}
}
Enquanto PrintPrimaryNumbersAsync() é decorado pela keyword async ele chama getPrimesAsync()” de forma assíncrona. Uma vez que é chamado, a execução imediatamente retorna para a thread principal e uma vez que qualquer uma das outras threads é finalizada, a thread principal terá o controle de volta (no nosso caso, escrever os números principais que podem ser encontrados no intervalo fornecido).
Agora veremos o código Main:static void Main(string[] args)
{
DateTime t1 = DateTime.Now;
PrintPrimaryNumbers();
var ts1 = DateTime.Now.Subtract(t1);
Trace.WriteLine("Finished Sync and started Async");
var t2 = DateTime.Now;
PrintPrimaryNumbersAsync();
var ts2 = DateTime.Now.Subtract(t2);
Trace.WriteLine(string.Format(
"It took {0} for the sync call and {1} for the Async one", ts1, ts2));
Trace.WriteLine("Any Key to terminate!!");
}
Você pode me dizer a diferença entre ts1 e ts2 ?
Aqui está o resultado :
Finished Sync and started Async
Demorou 00:32:16.1627422 para a chamada síncrona e 00:00:00.0050003 para a chamada assíncrona.