Je ne comprends pas très bien la différence entre Task.Wait
et await
.
J'ai quelque chose de similaire aux fonctions suivantes dans un service 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
}
}
Où Get
sera l'impasse.
Qu'est-ce qui pourrait provoquer cela? Pourquoi cela ne pose-t-il pas un problème lorsque j'utilise une attente de blocage plutôt que await Task.Delay
?
Task.Delay(1).Wait()
est fondamentalement la même chose que Thread.Sleep(1000)
. Dans le code de production réel, il est rarement approprié.
WaitAll
est à l'origine de l'impasse. Voir le lien vers mon blog dans ma réponse pour plus de détails. Vous devez utiliser à la await Task.WhenAll
place.
ConfigureAwait(false)
un seul appel àBar
Ros
impasse ou ne la bloquerez pas, mais parce que vous avez un énumérable qui en crée plus d'une et attend ensuite toutes, la première barre bloquera la seconde. Si vous await Task.WhenAll
au lieu d'attendre toutes les tâches, afin de ne pas bloquer le contexte ASP, vous verrez la méthode retourner normalement.
.ConfigureAwait(false)
tout le long de l'arborescence jusqu'à ce que vous bloquiez, de cette façon, rien n'essaye jamais de revenir au contexte principal; Ça marcherait. Une autre option serait de faire tourner un contexte de synchronisation interne. Lien . Si vous mettez le Task.WhenAll
dans un, AsyncPump.Run
il bloquera efficacement le tout sans que vous ayez besoin de ConfigureAwait
quelque part, mais c'est probablement une solution trop complexe.
Task.Delay(1).Wait()
ce qui est assez bon.