Por que webclient.DownloadStringTaskasync () Block? - Novo API/Sintaxe assíncrona/CTP
-
27-09-2019 - |
Pergunta
Por alguma razão, há uma pausa após o início do programa. Acredito que WebClient().DownloadStringTaskAsync()
é a causa.
class Program
{
static void Main(string[] args)
{
AsyncReturnTask();
for (int i = 0; i < 15; i++)
{
Console.WriteLine(i);
Thread.Sleep(100);
}
}
public static async void AsyncReturnTask()
{
var result = await DownloadAndReturnTaskStringAsync();
Console.WriteLine(result);
}
private static async Task<string> DownloadAndReturnTaskStringAsync()
{
return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
}
}
Tanto quanto eu entendo, meu programa deve começar a contar de 0 a 15 imediatamente. Estou fazendo algo errado?
Eu tive o mesmo problema com a amostra original de download da Netflix (que você obtém com CTP) - Depois de pressionar o botão de pesquisa, a UI primeiro congela - e depois de algum tempo, ele responde ao carregar os próximos filmes. E acredito que não congelou na apresentação de Anders Hejlsberg no PDC 2010.
Mais uma coisa. Quando em vez de
return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov"));
Eu uso meu próprio método:
return await ReturnOrdinaryTask();
Qual é:
public static Task<string> ReturnOrdinaryTask()
{
var t = Task.Factory.StartNew(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("------------- " + i.ToString());
Thread.Sleep(100);
}
return "some text";
});
return t;
}
Funciona como deveria. Quero dizer, não carrega nada, mas começa imediatamente e não bloqueia o fio principal, enquanto faz seu trabalho.
Editar
Ok, o que eu acredito agora é: o WebClient.DownloadStringTaskAsync
A função está estragada. Deve funcionar sem o período inicial de bloqueio, assim:
static void Main(string[] args)
{
WebClient cli = new WebClient();
Task.Factory.StartNew(() =>
{
cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
cli.DownloadStringAsync(new Uri("http://www.weather.gov"));
});
for (int i = 0; i < 100; i++)
{
Console.WriteLine(i);
Thread.Sleep(100);
}
}
Solução
Embora o seu programa bloqueie por um tempo, ele retoma a execução no loop for, antes que o resultado seja retornado do servidor remoto.
Lembre-se de que a nova API assíncrona ainda é um thread único. Então WebClient().DownloadStringTaskAsync()
ainda precisa ser executado em seu tópico até que a solicitação tenha sido preparada e enviada ao servidor, antes que ele possa await
e render a execução de volta ao fluxo do seu programa em Main ().
Eu acho que os resultados que você está vendo são devido ao fato de levar algum tempo para criar e enviar a solicitação da sua máquina. Primeiro quando isso terminar, a implementação de DownloadStringTaskAsync
Pode aguardar a conclusão da IO da rede e do servidor remoto e pode retornar a execução a você.
Por outro lado, seu RunOrdinaryTask
O método apenas inicializa uma tarefa e fornece uma carga de trabalho e diz para iniciar. Então ele retorna imediatamente. É por isso que você não vê um atraso ao usar RunOrdinaryTask
.
Aqui estão alguns links sobre o assunto: Blog de Eric Lippert (um dos designers de idiomas), bem como Postagem inicial do blog de Jon Skeet sobre isso. Eric tem uma série de 5 posts sobre o estilo de passagem de continuação, que realmente é o que async
e await
é realmente sobre. Se você deseja entender o novo recurso em detalhes, pode ler as postagens de Eric sobre CPS e Async. De qualquer forma, ambos os links acima fazem um bom trabalho ao explicar um fato muito importante:
- Assíncrono! = Paralelo
Em outras palavras, async
e await
Não gira novos threads para você. Eles apenas permitem retomar a execução do seu fluxo normal, quando você está fazendo uma operação de bloqueio - vezes em que sua CPU apenas se senta e não fazia nada em um programa síncrono, aguardando a conclusão de alguma operação externa.
Editar
Só para ficar claro sobre o que está acontecendo: DownloadStringTaskAsync
configura uma continuação e depois liga WebClient.DownloadStringAsync
, no mesmo tópico, e então rende a execução de volta ao seu código. Portanto, o tempo de bloqueio que você está vendo antes que o loop comece a contar, é o tempo que leva DownloadStringAsync
completar. Seu programa com assíncrona e aguardar está muito perto de ser o equivalente ao programa a seguir, que exibe o mesmo comportamento que seu programa: um bloco inicial, depois a contagem começa e em algum lugar do meio, o Async Op termina e imprime o conteúdo do A URL solicitada:
static void Main(string[] args)
{
WebClient cli = new WebClient();
cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result);
cli.DownloadStringAsync(new Uri("http://www.weather.gov")); // Blocks until request has been prepared
for (int i = 0; i < 15; i++)
{
Console.WriteLine(i);
Thread.Sleep(100);
}
}
Nota: Não sou de forma alguma um especialista nesse assunto, então posso estar errado em alguns pontos. Sinta -se à vontade para corrigir minha compreensão do assunto, se você acha que isso está errado - apenas olhei para a apresentação do PDC e joguei com o CTP na noite passada.
Outras dicas
Tem certeza de que o problema não está relacionado às configurações de proxy que estão sendo detectadas no IE/Registry/Somewhere Slow?
Tente definir webclient.proxy = nulo (ou especificar configurações no app.config) e seu período de "bloqueio" deve ser mínimo.
Você está pressionando F5 ou CTLR+F5 para executá -lo? Com F5, há um atraso para vs apenas para procurar os símbolos para asyncctplibrary.dll ...