Разбор порядка выполнения асинхронных вызовов в C#

В консольном приложении без особого контекста общее ожидание составит примерно 2 секунды, а не 2,1: Task.Delay идёт одновременно с Thread.Sleep.

Короткий ответ

Что ответить на собеседовании

В консольном приложении без особого контекста общее ожидание составит примерно 2 секунды, а не 2,1: Task.Delay идёт одновременно с Thread.Sleep.

Подробный разбор

Ответ с пояснениями

Условие

Как будет происходить исполнение представленного фрагмента и какие временные задержки при этом возникнут?

var operation = PerformAsync();
Thread.Sleep(2000);

await operation;

static async Task PerformAsync()
{
    await Task.Delay(100);
}

Ответ

В консольном приложении без особого контекста общее ожидание составит примерно 2 секунды, а не 2,1: Task.Delay идёт одновременно с Thread.Sleep.

PerformAsync начинает выполняться сразу и доходит до незавершённого Task.Delay(100). Затем вызывающий поток блокируется на 2000 мс. К моменту await операция обычно уже завершилась, поэтому дополнительной заметной задержки нет.

В UI-приложении продолжение может требовать тот же заблокированный поток: тогда задача окончательно завершится только после его освобождения. Thread.Sleep замораживает UI, поэтому в асинхронном коде предпочтительнее await Task.Delay. Метка javascript в условии ошибочна: это C#; тайминги приблизительные и зависят от планировщика.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее