Nie do końca rozumiem różnicę między Task.Wait
i await
.
Mam coś podobnego do następujących funkcji w usłudze WebAPI ASP.NET:
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
}
}
Gdzie Get
będzie impas.
Co może to powodować? Dlaczego nie powoduje to problemów, gdy używam oczekiwania blokującego zamiast await Task.Delay
?
Task.Delay(1).Wait()
jest w zasadzie dokładnie tym samym co Thread.Sleep(1000)
. W rzeczywistym kodzie produkcyjnym rzadko jest to właściwe.
WaitAll
powoduje impas. Zobacz link do mojego bloga w mojej odpowiedzi, aby uzyskać więcej informacji. Zamiast tego powinieneś użyć await Task.WhenAll
.
ConfigureAwait(false)
do pojedynczego połączenia do Bar
lub Ros
nie impasu, ale ponieważ masz przeliczalny, który jest tworzenie więcej niż jeden, a następnie czeka na wszystkich tych, pierwszy pasek impasu drugi. Jeśli await Task.WhenAll
zamiast czekać na wszystkie zadania, aby nie zablokować kontekstu ASP, zobaczysz, że metoda powraca normalnie.
.ConfigureAwait(false)
całego drzewa w górę, aż do momentu zablokowania, w ten sposób nic nigdy nie próbuje wrócić do głównego kontekstu; to powinno działać. Inną opcją byłoby rozwinięcie wewnętrznego kontekstu synchronizacji. Link . Jeśli umieścisz Task.WhenAll
w sposób AsyncPump.Run
zostanie ona skutecznie blokować na całość bez potrzeby ConfigureAwait
gdziekolwiek, ale to prawdopodobnie nadmiernie skomplikowane rozwiązanie.
Task.Delay(1).Wait()
co jest wystarczająco dobry.