"Async All the Way Down": Ну, что же происходит на дне?

Я пытаюсь в полной мере понять async - await, и один из пробелов в моем понимании - это то, что есть "All the Way Down". Я создаю метод async, он вызывается другим методом async и т.д., Вплоть до того, что я понимаю в неопределенных терминах, таких как "пользовательский интерфейс" или "веб-сервер, который может обрабатывать несколько запросов". Как бы я в технических терминах описал, что "все вниз"?

Итак, возьмем второй пример веб-сервера. Скажем, у меня есть действие контроллера, например

[HttpGet]
public async Task<IHttpActionResult> GetRecords()
{
    var records = await repository.GetRecordsFromDbAsync();
    return Ok(records);
}

Где я могу найти в исходном коде .NET код "all the way down", который позволяет это вызывать асинхронно?

Ответ 1

Каждый процедурный язык программирования представляет собой серию вызовов функций/процедур, при этом одна функция/процедура вызывает другую. Можно представить эту последовательность вызовов от процедуры к процедуре, используя граф вызовов см. Wikipedida для начальной точки для графиков вызовов. На этих графиках обычно показана последовательность процедурных вызовов, начинающихся сверху и идущих в нижнюю часть.

Я быстро создал диаграмму частичного графика вызовов для приложения ASP.NET MVC. Диаграмма является лишь частичным представлением для приложения ASP.NET MVC, поскольку она не позволяет такие вещи, как первоначальный прием запроса операционной системой (например, Windows), веб-сервер (например, IIS) и различные компоненты ASP.NET. которые отвечают за обработку HTTP-запроса, когда он проходит через конвейер обработки запросов ASP.NET. Эти вопросы могут быть опущены для целей этого обсуждения. Хотя стоит отметить, что они будут сидеть в верхней части графика вызовов, потому что они имеют дело с начальными этапами обработки HTTP-запроса, и в конечном итоге в какой-то момент ASP.NET в конечном итоге вызывает действие контроллера.

Как вы можете видеть на диаграмме, я представил действие контроллера, который будет вызываться ASP.NET как действие async. В левой части графика вызовов есть последовательность асинхронных процедур. В конечном счете он приходит в коробку, которая является предметом вашего вопроса.

Ответ на вопрос, который согласуется с понятием "Весь путь вниз", заключается в том, что "я асинхронный метод". Что делает этот асинхронный метод? Ну, это зависит от того, что вы хотите сделать? Если вы читаете или записываете файл, тогда это асинхронный вызов для чтения или записи файла. Вы делаете запрос к базе данных? Затем вызов представляет собой метод async, который делает этот запрос. Имея это в виду, я думаю, вы можете сказать, что часто то, что находится внизу, будет методом драйвера устройства, который выполняет асинхронный ввод ввода-вывода. Несмотря на то, что это может быть простой асинхронный процесс, связанный с вычислением, который вы хотите выполнить, например, обработку изображения или видеофайл.

Стоит также отметить правую часть графика вызовов. Хотя часто вы можете использовать методы async до конца, правая ветвь этого графика вызовов показывает, что вам необязательно вообще вызывать метод асинхронного метода внизу. введите описание изображения здесь

Ответ 2

Фраза "async all the down" немного вводит в заблуждение, потому что обычно это относится к тому факту, что, как только вы используете метод async, вам необходимо иметь методы асинхронного копирования (или обратно, в зависимости от вашего умственного изображение) - от вашего асинхронного метода до его вызывающего абонента, а затем вызывающего абонента и т.д. назад.

В этом примере показан контроллер WebApi/MVC, который предоставляет задачу async. Следующим шагом в цепочке async является инфраструктура WebApi/MVC, которая получает запрос HTTP GET, сопоставляет его с контроллером и отправляет вызов методу контроллера. Эта инфраструктура async осведомлена, то есть знает, что правильно вывести методы управления async и await их результат, чтобы вернуть ответ HTTP.

Что касается того, как именно реализована эта инфраструктура, я не знаю конкретно и не забочусь - я знаю, что веб-службы ASP.NET поддерживают контроллеры async Task, и это достаточно хорошо для меня.

Ответ 3

Весь путь вниз будет Win32 API. На этом уровне асинхронный ввод-вывод выполняется с помощью объектов OVERLAPPED и предупреждающих ожиданий, таких как WaitForMultipleObjectsEx.

Ответ 4

GetRecordsFromDbAsync - это асинхронный метод, поэтому ваш асинхронный метод верхнего уровня (который вызывается веб-сервером ASP.NET с поддержкой async) просто передает свою асинхронность на следующий уровень.

Всюду до тех пор, пока GetRecordsFromDbAsync или ее потомки фактически не вызовут последний метод async в стеке вызовов. Там он предположительно становится родным и регистрирует прерывание ввода-вывода или что-то еще, которое вызывается при чтении файла, обрабатывается веб-запрос и т.д.

Ответ 5

Из вашего вопроса неясно, где вы нашли эту цитату или в каком контексте она используется. Однако "асинхронный путь вниз" обычно понимается как означающий, что вы не должны переключаться между синхронными и асинхронными методами в одной логической операции.

Если что-то должно выполняться асинхронно, оно должно быть последовательно асинхронным на всех уровнях - таким образом, "полностью вниз" (и вверх) иерархии/стек вызовов /etc. Другими словами, никогда не имеет смысла делать синхронный блокирующий вызов в любой точке асинхронного кода, потому что тогда он вообще не асинхронен.

Из записи блога команды .NET Parallel Programming, "Должен ли я выставлять синхронные обертки для асинхронных методов?" :

Async All the Way Down

Дело здесь в том, что вам нужно быть предельно осторожным при переносе асинхронных API как синхронных, как если бы вы не были осторожны, вы могли столкнуться с реальными проблемами. Если вы когда-нибудь подумали, что вам нужно вызвать асинхронный метод из чего-то, ожидающего синхронного вызова (например, вы реализуете интерфейс, который имеет синхронный метод, но для реализации этого интерфейса вам нужно использовать функциональные возможности, которые подвергаются асинхронному воздействию), сначала убедитесь, что это действительно, действительно необходимо; в то время как может показаться более целесообразным обернуть "синхронизацию по асинхронному", а не повторять то или иное кодовое число, чтобы быть асинхронным сверху вниз, рефакторинг часто является лучшим долгосрочным решением, если это возможно.

Что касается вашего вопроса о том, где это можно найти в исходном коде .NET, MSalters уже ответили на это. В принципе, API-интерфейсы .NET будут обращаться к API-интерфейсам на системном уровне, предоставляемым операционной системой Windows. Они выполняют асинхронный ввод-вывод и сигнализируют вызывающему абоненту, когда они завершены. Вам действительно не нужно понимать, как это работает на техническом уровне; это вся точка абстракции.