Não entendo bem a diferença entre Task.Wait
e await
.
Eu tenho algo semelhante às seguintes funções em um serviço ASP.NET WebAPI:
public class TestController : ApiController
{
public static async Task<string> Foo()
{
await Task.Delay(1).ConfigureAwait(false);
return "";
}
public async static Task<string> Bar()
{
return await Foo();
}
public async static Task<string> Ros()
{
return await Bar();
}
// GET api/test
public IEnumerable<string> Get()
{
Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());
return new string[] { "value1", "value2" }; // This will never execute
}
}
Onde Get
será um impasse.
O que poderia causar isso? Por que isso não causa um problema quando eu uso uma espera de bloqueio em vez de await Task.Delay
?
Task.Delay(1).Wait()
é basicamente a mesma coisa que Thread.Sleep(1000)
. No código de produção real, raramente é apropriado.
WaitAll
está causando o impasse. Veja o link para o meu blog na minha resposta para mais detalhes. Você deve usar em seu await Task.WhenAll
lugar.
ConfigureAwait(false)
uma chamada únicaBar
ou Ros
não entra em conflito, mas porque possui um enumerável que está criando mais de uma e aguardando todas, a primeira barra entra em impasse na segunda. Se você, em await Task.WhenAll
vez de esperar em todas as tarefas, para não bloquear o contexto ASP, verá o método retornar normalmente.
.ConfigureAwait(false)
todo o caminho até a árvore até que você bloquear, dessa forma nada está sempre tentando voltar para o contexto principal; Isso funcionaria. Outra opção seria ativar um contexto de sincronização interno. Link . Se você colocar o item Task.WhenAll
em, AsyncPump.Run
ele efetivamente bloqueará a coisa toda sem a necessidade de ir a ConfigureAwait
qualquer lugar, mas provavelmente é uma solução excessivamente complexa.
Task.Delay(1).Wait()
que é bom o suficiente.