Что такое синхронное программирование?
Что проверяют
Понимаете ли вы, что синхронный вызов держит поток до конца операции, и во что это обходится в реальном .NET-приложении. Следом почти всегда просят сравнить с async/await и спрашивают, что будет, если вызвать .Result у задачи.
Как отвечать
Синхронная модель: операции идут строго одна за другой, и вызывающий код не продолжается, пока метод не вернёт результат. Для вычислений на процессоре это нормально, поток занят полезной работой. Проблемы начинаются на вводе-выводе:
- блокировка потока:
File.ReadAllText, синхронныйExecuteReaderилиHttpClientчерез.Resultдержат поток, пока диск, сеть или база не ответят. Поток ничего не делает, но и отдать его другой работе нельзя; - UI-приложения: в WPF или WinForms долгий синхронный вызов в обработчике кнопки замораживает окно, потому что UI-поток один;
- ASP.NET Core: каждый заблокированный запрос держит поток пула. Под нагрузкой пул добавляет потоки медленно, наступает thread pool starvation, и время ответа растёт при почти пустом процессоре;
- асинхронная альтернатива:
await File.ReadAllTextAsync(...)возвращает поток в пул на время ожидания, продолжение выполнится, когда операция завершится.
Разведите понятия: синхронность и однопоточность это разные оси. Синхронный код может работать во многих потоках, а await на вводе-выводе вообще не занимает поток, пока ждёт.
Закончите тем, где синхронный код уместен: консольная утилита, короткие вычисления, места, где простота отладки важнее пропускной способности.
Где спотыкаются
Путают асинхронность с многопоточностью и говорят, что async «запускает метод в другом потоке». На деле метод до первого незавершённого await выполняется синхронно в вызывающем потоке.
Предлагают «сделать асинхронно» через Task.Run вокруг синхронного запроса к базе. В серверном коде это просто занимает второй поток вместо первого.
Не знают про sync-over-async: .Result и .Wait() при наличии SynchronizationContext (WPF, классический ASP.NET) дают дедлок, а в ASP.NET Core ведут к истощению пула.