Ожидание против Задачи. Подождите - Тупик?

Я не совсем понимаю разницу между 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?

Ответ 1

Wait и await - в то время как аналогичный концептуально - на самом деле совершенно разные.

Wait будет блокироваться синхронно до завершения задачи. Таким образом, текущий поток буквально заблокирован, ожидая завершения задачи. Как правило, вы должны использовать " async all the down"; то есть не блокировать async код. В моем блоге я расскажу о том, как блокировка в асинхронном коде вызывает тупик.

await асинхронно ждать, пока не завершит задачу. Это означает, что текущий метод "приостановлен" (его состояние зафиксировано), и метод возвращает неполную задачу своему вызывающему. Позже, когда выражение await завершено, оставшаяся часть метода запланирована как продолжение.

Вы также упоминали "кооперативное блок", с помощью которого я предполагаю, вы имеете в виду задачу, которую вы Wait ИНГ на может выполнять на резьбе ожидания. Бывают ситуации, когда это может произойти, но это оптимизация. Существует много ситуаций, когда этого не может быть, например, если задача предназначена для другого планировщика, или если она уже запущена или если она не является кодовой задачей (например, в вашем примере кода: Wait не может выполнить задачу Delay inline, потому что там нет кода для него).

Вы можете найти мое async/await введение.

Ответ 2

Исходя из того, что я читал из разных источников:

Выражение ожидания не блокирует поток, на котором он выполняется. Вместо этого он заставляет компилятор подписывать остальную часть асинхронного метода как продолжение на ожидаемой задаче. Затем элемент управления возвращается к вызывающей стороне метода async. Когда задача завершается, она вызывает свое продолжение, и выполнение метода async возобновляется там, где оно было остановлено.

Чтобы дождаться завершения одной задачи, вы можете вызвать ее метод Task.Wait. Вызов метода Wait блокирует вызывающий поток до тех пор, пока экземпляр одного класса не завершит выполнение. Метод без параметров Wait() используется для безоговорочного ожидания до завершения задачи. Задача имитирует работу, вызывая метод Thread.Sleep для сна в течение двух секунд.

Эта статья также хорошо читается.