var result = await DoWorkAsync();
Console.WriteLine(result);
разворачивается компиляторов в
var awaiter = DoWorkAsync();
awaiter.OnCompleted(() =>
var result1 = awaiter.GetResult();
Console.WriteLine(result);
)
Получается допустим работают два потока: основной и поток из тредпула
Основной доходит до вызова функции, заходит в DoWorkAsync(), и почти моментально оттуда выходит и может дальше работать.
На этом этапе DoWorkAsync() начинает исполнять поток из тредпула.
Основной поток ожидает признак продолжения, для того, чтобы выполнить операторы дальше.
Вот тут не понятно: основной поток ждет - означает ли это то, что он заблокирован? Или там под капотом как-то хитро устроено, что поток живой, но просто курит, пока поток из пула не скажет, мол я закончил работу - продолжай
Все треды свободны, если у тебя по-настоящему асинхронная работа с IO, а не только вычисления. https://blog.stephencleary.com/2013/11/there-is-no-thread.html
Я читаю вот его книгу как раз Что означает асинхронная IO ? Подключение к базе данных это IO. Как я понимаю, асинхронность в этом случае заключается в том, что поток, который инициирует подключение - возвращается в тредпул на то время, пока БД не подключится, если не применять ConfigureAwait(false);
Для начала нужно понять, что такое стейт машина. Самая простая стейт машина это IEnumerator<T> Это просто метод MoveNext() и свойство Current. Теперь самое интересное - пишем цикл foreach, который работает с этим интерфейсом под капотом. Для пользователя это интерфейс IEnumerable<T>, но он сразу возвращает IEnumerator<T>, и дальше работа идет только с этим интерфейсом. Посмотрите этот интерфейс, и попробуйте понять, что именно компилятор делает. Посмотрите il, который генерится. Попробуйте реализовать свой IEnumerator<T> с помощью yield return, а потом попробуйте написать ручками тот же самый код. Получится, что вам прийдется сохранить состояние, и использовать в методе MoveNext() оператор switch case, который будет это состояние использовать. То есть код foreach метода на самом деле разбивается на куски, которые вызываются по отдельности в рамках отдельных клауз switch case, в зависимости от состояния. С async/await происходит подобная история - async await это всего лишь синтаксический сахар к стейт машине. То есть, не просто генерится код, который тут же выходит - вызывающий метод это тоже стейтмашина. И кто-то должен вызывать метод MoveNext() много раз подряд. В промежутках между этими вызовами никакого потока нет. Этот “кто-то”, который вызывает ваш код снова и снова происходит во взятом из тредпула треде. По сути можно считать, что это каждый раз новый тред, хотя это на самом деле просто спящие треды переиспользуются из тредпула. А пробуждает этот тред прерывание, которое зарегистрировано в каком-либо драйвере. То есть, в промежутке нет никакого потока.
То есть, ваш пример кода валидный, но он не вполне объясняет, что происходит. Следует понимать, что он находится в рамках клаузы case внутри switch, и за один вход в метод MoveNext выполняется только одна клауза, потом следует return. ``` MoveNext() { switch (this._state) { case START: var awaiter = DoWorkAsync(); this._state = WORK_STARTED; break; case WORK_STARTED: var result1 = awaiter.GetResult(); Console.WriteLine(result); break; } } ```
Обсуждают сегодня