Я не зовсім розумію різницю між Task.Wait
і await
.
У сервісі 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
}
}
Де Get
буде тупик.
Що може спричинити це? Чому це не викликає проблем, коли я використовую блокування очікування, а не await Task.Delay
?
Task.Delay(1).Wait()
в основному точно те саме, що і Thread.Sleep(1000)
. У фактичному виробничому коді це рідко доцільно.
WaitAll
причиною стає тупик. Дивіться посилання на мій блог у моїй відповіді для отримання більш детальної інформації. Ви повинні використовувати await Task.WhenAll
замість цього.
ConfigureAwait(false)
в єдиний виклик Bar
або Ros
не безвихідь, а тому , що у вас є перелічуваних , що створює більше , ніж один , а потім чекати на всіх тих, перша смуга буде тупикової другий. Якщо ви await Task.WhenAll
замість того, щоб чекати всіх завдань, щоб не блокувати контекст ASP, ви побачите, як метод повертається нормально.
.ConfigureAwait(false)
всю дорогу вгору по дереву, поки ви не заблокуєте, таким чином нічого не намагається повернутися до основного контексту; це спрацювало б. Іншим варіантом було б розкрутити внутрішній контекст синхронізації. Link . Якщо ви помістите Task.WhenAll
в нього, AsyncPump.Run
він ефективно блокує всю справу, не потребуючи ConfigureAwait
нікуди, але це, мабуть, надмірно складне рішення.
Task.Delay(1).Wait()
чим досить добре.