Когда используется пул потоков?



Так что у меня есть понимание того, как узел.js works: он имеет один поток прослушивателя, который получает событие, а затем делегирует его в рабочий пул. Рабочий поток уведомляет прослушиватель, как только он завершает работу, а затем прослушиватель возвращает ответ вызывающему объекту.



мой вопрос таков: если я стою на HTTP-сервере в узле.js и вызов сна на одном из моих событий маршрутизируемого пути (например, "/test/sleep"), вся система останавливается. Даже один поток слушателя. Но я понимал, что этот код происходит в рабочем пуле.



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



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

502   3  

3 ответов:

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

на данный момент мы будем игнорировать явную многопоточность/многопоточность кластер и webworker-threads, и просто говорить о типичных без резьбы узел.

узел выполняется в одном цикле событий. Он однопоточный, и вы получаете только один поток. Весь javascript, который вы пишете, выполняется в этом цикле, и если в этом коде происходит операция блокировки, то он блокирует весь цикл, и ничего больше не произойдет, пока он не закончится. Это обычно однопоточный характер узла, о котором вы так много слышите. Но, это не вся картина.

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

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


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

  • любой внешний модуль, который вы включите в свой проект, который использует собственный C++ и libuv, вероятно, будет использовать пул потоков (подумайте: доступ к базе данных)
  • libuv имеет размер пула потоков по умолчанию 4 и использует очередь для управления доступом к пулу потоков - результат заключается в том, что если у вас есть 5 длительных запросов БД, которые все идут одновременно, один из них (и любое другое асинхронное действие, которое зависит от пула потоков) будет ждать завершения этих запросов, прежде чем они даже начнут
  • вы можно смягчить путем увеличения размера пула потоков через UV_THREADPOOL_SIZE переменная окружения, если вы делаете это до того, как пул потоков требуется и создается:process.env.UV_THREADPOOL_SIZE = 10;

если вы хотите традиционную мульти-обработку или многопоточность в узле, вы можете получить его через встроенный cluster модуль или различные другие модули, такие как вышеупомянутый webworker-threads, или вы можете подделать его, реализовав какой-то способ фрагментации вашей работы и вручную используя setTimeout или setImmediate или process.nextTick чтобы приостановить работу и продолжить ее в более позднем цикле, чтобы завершить другие процессы (но это не рекомендуется).

обратите внимание, что если вы пишете длинный рабочий/блокирующий код в javascript, вы, вероятно, делаете ошибку. Другие языки будут работать гораздо эффективнее.

так что у меня есть понимание того, как узел.js works: он имеет один поток прослушивателя, который получает событие, а затем делегирует его в рабочий пул. Рабочий поток уведомляет прослушиватель, как только он завершает работу, а затем прослушиватель возвращает ответ вызывающему объекту.

это не совсем так. Узел.js имеет только один" рабочий " поток, который выполняет выполнение javascript. Есть потоки внутри узла, которые обрабатывают обработку ввода-вывода, но думать их как "рабочих" - это заблуждение. На самом деле есть только обработка ввода-вывода и несколько других деталей внутренней реализации узла, но как программист вы не можете влиять на их поведение, кроме нескольких параметров misc, таких как MAX_LISTENERS.

мой вопрос таков: если я встану на HTTP-сервер в узле.js и вызов сна на одном из моих событий маршрутизируемого пути (например, "/test/sleep"), вся система останавливается. Даже один поток слушателя. Но мое понимание было что этот код происходит в рабочем пуле.

в JavaScript нет механизма сна. Мы могли бы обсудить это более конкретно, если бы вы опубликовали фрагмент кода того, что, по вашему мнению, означает "сон". Там нет такой функции, чтобы вызвать для имитации что-то вроде time.sleep(30) в python, например. Есть setTimeout но это принципиально не сон. setTimeout и setInterval явно релиз, не блок, цикл событий, поэтому другие биты кода могут выполняться на основном поток выполнения. Единственное, что вы можете сделать, это задействовать процессор с вычислением в памяти, что действительно приведет к голоду основного потока выполнения и сделает вашу программу невосприимчивой.

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

сетевой ввод-вывод всегда асинхронный. Конец истории. Disk IO имеет как синхронные, так и асинхронные API, поэтому нет никакого "решения". узел.js будет вести себя в соответствии с основными функциями API, которые вы называете sync vs normal async. Например: fs.readFile vs fs.readFileSync. Для дочерних процессов, существуют также отдельные child_process.exec и child_process.execSync API-интерфейсы.

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

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

... так что не блокируйте его.

Comments

    Ничего не найдено.