2015-03-27 2 views
5

Посмотрел еще на несколько вопросов, но не нашел то, что искал. Я использую Scala, но мои вопросы очень высоки, и, как мы надеемся, агностик любого языка.Как работают обратные вызовы в неблокирующем дизайне?


регулярный сценарий:

  1. Thread A запускает функцию, и есть некоторые блокировки работы предстоит сделать (скажем, БД вызова).
  2. Функция имеет некоторый неблокирующий код (например, Async блок в Scala), чтобы вызвать своего рода «рабочий» Thread B (в другом пуле), чтобы забрать задачу ввода-вывода.
  3. Метод в Thread A завершает возвращение Будущего, которое в конечном итоге будет содержать результат, и Thread A возвращается в свой бассейн, чтобы быстро подобрать другой запрос для обработки.

Q1. Какой-то поток где-то обычно должен ждать?

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

Это правильное общее понимание?

Q2. Как работает обратный вызов?

В приведенных выше сценариях - Thread B, что делает работу I/O будет запустить функцию обратного вызова (предоставленный Thread А), если/когда работа ввода/вывода в эксплуатацию - который завершает Future с каким-то результатом.

Нить А теперь не работает с другим, и больше не связана с первоначальным запросом. Как результат в будущем отправляется обратно в клиентский сокет? Я понимаю, что разные языки имеют разные реализации такого механизма, но на высоком уровне мое текущее предположение заключается в том, что (независимо от языка/структуры) некоторые объекты framework/container всегда должны выполнять какую-то оркестровку, чтобы при выполнении задачи «Будущее» завершен. Результат отправляется обратно в исходный сокет, обрабатывающий запрос.


Я потратил часы, пытаясь найти статьи, которые объяснят это, но каждая статья, похоже, имеет дело с реальными деталями низкого уровня. Я знаю, что я пропустил некоторые детали, но мне сложно задавать свой вопрос, потому что я не совсем уверен, какие части меня не пропали :)

ответ

3

Мое понимание неблокируемых архитектуры является то, что общий подход к еще есть темы ожидания/блокировки на работе I/O где

Если поток становится заблокирован где-то, это не совсем неблокирующая архитектура. Так что нет, это не совсем правильное понимание этого. Это не значит, что это обязательно плохо. Иногда вам просто приходится иметь дело с блокировкой (например, с помощью JDBC). Лучше было бы оттолкнуть его в фиксированный пул потоков, предназначенный для блокировки, а не позволять всему приложению страдать от голода.

Нить А теперь не работает с чем-то еще и больше не связана с первоначальным запросом. Как результат в будущем отправляется обратно в клиентский сокет?

Использование Future s, это действительно зависит от ExecutionContext. Когда вы создаете Future, где работа выполняется, это зависит от ExecutionContext.

val f: Future[?] = ??? 
val g: Future[?] = ??? 

f и g созданы сразу, и работа представляется в очереди задач в ExecutionContext. Мы не можем гарантировать, что в большинстве случаев они будут выполняться или выполняться сначала. То, что вы делаете со значениями, хорошо. Очевидно, что если вы используете Await, чтобы дождаться завершения Future s, мы заблокируем текущий поток. Если они map их и сделают что-то со значениями, то нам снова понадобится еще ExecutionContext, чтобы отправить задание. Это дает нам цепочку задач, которые асинхронно передаются и повторно отправляются исполнителю для выполнения каждый раз, когда мы манипулируем Future.

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

+0

m-z, спасибо за комментарии. Я получаю 1. но все еще не хватает чего-то на 2. У меня есть лучшее понимание Future и ExecutionContext, теперь спасибо. Я думаю, что моя проблема на более высоком уровне, возможно, то есть. как результат успешного завершения объекта Future в куче приводит к выходному потоку? - Я предполагаю, что есть какой-то объект Response, связанный с исходным Будущим, который заставляет выходной поток быть обратно обратно через сокет сервера. Я использую Play/Netty, поэтому, я думаю, мне нужно проверить документы. – JamieP

4

Q1: Нет, по крайней мере, на уровне кода пользователя. Надеемся, ваш асинхронный ввод-вывод в конечном итоге сводится к асинхронному API ядра (например, select()). Это, в свою очередь, будет использовать DMA для ввода/вывода и запуска прерывания, когда это будет сделано. Таким образом, это асинхронно, по крайней мере, до уровня аппаратного обеспечения.

Q2: Thread B завершает Future. Если вы используете что-то вроде onComplete, то поток B запускает это (возможно, путем создания новой задачи и передачи этой задачи в пул потоков, чтобы забрать ее позже) в качестве части завершения вызова.Если другой поток вызвал Await для блокировки на Future, он запустит возобновление этого потока. Если ничто не обратилось к Future, ничего особенного не произойдет - значение находится там в Future, пока что-то его не использует. (См. PromiseCompletingRunnable для подробных подробностей - это удивительно читаемо).

+0

Иммунитесь, спасибо за ваши комментарии. Как и в моем комментарии к m-v выше, Im все еще не совсем понимает, как завершенное Будущее приводит к выходному потоку, - но тогда это более большой вопрос, чем просто обратные вызовы, которые я предполагаю. – JamieP

+0

'Future' не имеет никакого конкретного подключения к' OutputStream'. В случае спрей (с которым я больше знаком) даже не задействован «OutputStream», он использует актера, который в конечном итоге ведет переговоры с nio API. – lmm

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