25

(Возможно, это дублирует вопрос ASP.NET MVC4 Async controller - Why to use?, но про webapi, и я не согласен с ответами там)Асинхронные методы ApiController - какая прибыль? Когда использовать?

Предположим, у меня длинный SQL-запрос. Его данные должны быть сериализованы в JSON и отправлены в браузер (в качестве ответа на запрос xhr). Пример кода:

public class DataController : ApiController 
{ 
    public Task<Data> Get() 
    { 
     return LoadDataAsync(); // Load data asynchronously? 
    } 
} 

Что на самом деле происходит, когда я делаю $ .getJSON ('API/данные', ...) (см этот плакат http://www.asp.net/posters/web-api/ASP.NET-Web-API-Poster.pdf):

  1. [IIS] Запрос принимается IIS.
  2. [IIS] IIS ждет один поток [THREAD] из управляемого пула (http://msdn.microsoft.com/en-us/library/0ka9477y(v=vs.110).aspx) и начинает работать в нем.
  3. [THREAD] Webapi Создает новый объект DataController в этом потоке и других классах.
  4. [НИТИ] Использует задачи параллельный LIB для запуска SQL-запрос в [thread2]
  5. [НИТИ] восходит к управляемому пула, готовы к другой обработке
  6. [thread2] работает с SQL драйвера, считывает данные как он готов и вызывает [THREAD3] для ответа на запрос xhr.
  7. [THREAD3] отправляет ответ.

Пожалуйста, не стесняйтесь поправить меня, если что-то не так.

В приведенном выше вопросе, они говорят, точка и прибыль, что [thread2] не из управляемого пула, однако MSDN статья (ссылка выше) говорит, что

По умолчанию, параллельные типов библиотек например, Task и Task<TResult> использовать потоки потоков для запуска задач.

Поэтому я делаю вывод, что все ТРЕХ РЕЗЬБЫ из управляемого пула.

Кроме того, если бы я использовал синхронный метод, я бы все же оставил свой сервер отзывчивым, используя только один поток (из пула драгоценных потоков).

Итак, какова фактическая точка обмена от 1 потока до 3 потоков? Почему бы не просто увеличить потоки в пуле потоков?

Есть ли очевидные полезные способы использования асинхронных контроллеров?

ответ

29

Я думаю, что ключ недоразумение вокруг, как работают async задачи. У меня есть async intro в моем блоге, который может помочь.

В частности, a Task, возвращенный async, не имеет никакого кода. Скорее, это просто удобный способ оповестить вызывающих лиц о результате этого метода. Документы MSDN, которые вы цитируете, применимы только к задачам, которые фактически запускают код, например, Task.Run.

BTW, плакат, на который вы ссылаетесь, не имеет ничего общего с темами. Вот что происходит в запросе базы данных async (немного упрощено):

  1. Запрос принят IIS и передан в ASP.NET.
  2. ASP.NET принимает один из потоков потока потоков и назначает его этому запросу.
  3. WebApi создает DataController и т. Д.
  4. Действие контроллера запускает асинхронный SQL-запрос.
  5. Поток запроса возвращается в пул потоков. В настоящее время нет потоков, обрабатывающих запрос.
  6. Когда результат поступает с SQL-сервера, поток пула потоков считывает ответ.
  7. Этот поток потока потока уведомляет запрос о том, что он готов продолжить обработку.
  8. Поскольку ASP.NET знает, что ни один другой поток не обрабатывает этот запрос, он просто присваивает тот же поток запрос, чтобы он мог закончить его напрямую.

Если вы хотите, чтобы некоторые проверка концепции код, у меня есть an old Gist что искусственно ограничивает пул потоков ASP.NET с числом ядер (который является его минимальное значение), а затем делает N + 1 синхронные и асинхронные Запросы. Этот код просто делает задержку на секунду вместо обращения к SQL-серверу, но общий принцип тот же.

+1

«Когда результат поступает с SQL-сервера, поток пула потоков считывает ответ» - можете ли вы подробнее остановиться на этом? Где результат? Кто вызывает поток из threadpool? –

+2

Предполагая, что ваше SQL-соединение использует TCP/IP, результат поступает в виде сетевого пакета. Это вызывает прерывание, когда драйвер устройства считывает пакет и передает его в пользовательский режим. Механизм IOCP уведомляет пул потоков о том, что чтение сокета завершено, и оно обеспечит завершение ответа, проанализирует его и затем сообщит, что запрос готов к продолжению. (Это * все еще * слегка упрощено). –

+1

Меня особенно интересует «уведомляет пул потоков, который ...» AFAIK, когда что-то можно уведомить - он либо ожидает, либо уведомляет, либо периодически проверяет _notification queue_. Я делаю вывод, что для выполнения этих операций имеется выделенный выделенный поток 1+. Это верно? Должны ли мы считать в этом потоке ресурс «wasted-as-as-aync»? –

4

Прибыль от асинхронных действий заключается в том, что, пока контроллер ожидает завершения запроса sql, для этого запроса не выделяются потоки, тогда как если бы вы использовали синхронный метод, поток был бы заблокирован при выполнении этого метода из от начала до конца этого метода. Пока SQL-сервер выполняет свою работу, поток ничего не делает, кроме ожидания. Если вы используете асинхронные методы, этот же поток может отвечать на другие запросы, пока SQL-сервер делает свою работу.

Я считаю, что на шаге 4 шаги не соответствуют действительности, я не думаю, что он создаст новый поток для выполнения SQL-запроса. В 6 не создается новый поток, это только один из доступных потоков, который будет использоваться для продолжения, с которого остановился первый поток. Нить на 6 может быть такой же, как и начата операция async.

+0

Итак, я могу написать приложение, которое может запускать два одновременных sql-запроса, печатать их результаты и ** не будет использовать более одного потока в любое время **? Я хотел бы увидеть доказательство концепции для этого ответа. Ссылки или код. –

3

Я считаю, что следующее описывает явное преимущество асинхронных контроллеров над синхронными.

Веб-приложение с помощью синхронных методов для обслуживания высокой задержки вызовов, где пул потоков возрастает до максимума по умолчанию в .NET 4.5 из 5 , 000 нитей будет потреблять примерно 5 Гб больше памяти, чем приложения, способного на службе те же запросы, используя асинхронные методы и только 50 потоков. Когда вы выполняете асинхронную работу, вы не всегда используете поток. Например, когда вы выполняете асинхронный запрос веб-службы , ASP.NET не будет использовать нити между вызовом метода async и ожиданием. Использование потока пул для обслуживания запросов с высокой задержкой может привести к большой памяти след и плохое использование серверного оборудования.

из Using Asynchronous Methods in ASP.NET MVC 4

3

Точка async не должна делать многопоточное приложение, а позволяет одному однопоточному приложению выполнять что-то другое, а не ждать ответа от внешнего вызова, выполняемого в другом потоке или процессе.

Рассмотрите приложение для настольных компьютеров, которое показывает цены на акции с разных бирж. Приложение должно сделать пару вызовов REST/http, чтобы получить некоторые данные с каждого удаленного сервера обмена.

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

Мы могли бы запускать все многопоточные параллельные параллельные запросы и обновлять экран параллельно, но поскольку большая часть времени тратится на ожидание ответа с удаленного сервера это похоже overkill.

Возможно, было бы лучше для потока: Сделать запрос на первый сервер, но вместо того, чтобы ждать ответа, оставляйте маркер, место, где нужно вернуться, когда цены прибудут и перейдут к выдаче второго запроса , снова оставляя маркер места, чтобы вернуться к ... и т. д.

Когда все запросы были выпущены, поток выполнения приложения может продолжить работу с пользовательским вводом или тем, что когда-либо требуется.

Теперь, когда получен ответ от одного из серверов, поток может быть направлен для продолжения с ранее установленного маркера и обновления окна.

Все вышеперечисленное может быть закодировано длинной рукой, с одной резьбой, но было так ужасно многопоточно много проще было. Теперь процесс выхода из маркера и возврата выполняется компилятором, когда мы пишем async/wait. Все однопоточные.

Есть два ключевых момента здесь:

1) многопоточный это все еще может случиться! Обработка нашего запроса цен на акции происходит в другом потоке (на другой машине). Если бы мы делали db-доступ, то это было бы правдой. В примерах, где ожидание таймера, таймер работает в другом потоке. Наше приложение, хотя и однопоточное, точка выполнения просто скачет (контролируемым образом), в то время как внешние потоки выполняются

2) Мы теряем преимущество асинхронного выполнения, как только приложение требует завершения операции async. Рассмотрите приложение, показывающее цену кофе с двух обменов, приложение может инициировать запросы и обновлять свои окна асинхронно в одном потоке, но теперь, если приложение также рассчитало разницу в цене между двумя биржами, ему пришлось бы ждать асинхронного звонки для завершения. Это навязывается нам, потому что метод async (например, тот, который мы могли бы написать, чтобы вызвать обмен на цену акций) не возвращает цену акций, а задание, которое можно рассматривать как способ вернуться к маркеру, который было установлено, чтобы функция могла завершить и вернуть цену акций.

Это означает, что каждая функция, вызывающая функцию async, должна быть асинхронной или ждать завершения вызова «другой поток/процесс/машина» в нижней части стека вызовов, и если мы ожидаем дно звоните, чтобы закончить, зачем вообще беспокоиться о асинхронном режиме?

При написании веб-api, IIS или другого хоста является настольным приложением, мы пишем наши методы контроллера async, чтобы хост мог выполнять другие методы в нашем потоке для обслуживания других запросов, в то время как наш код ожидает ответа от работы на другой поток/процесс/машину.

Смежные вопросы