Что лучше для параллельных задач на node.js? Волокна? Веб-работники? или темы?


111

Некоторое время назад я наткнулся на node.js, и он мне очень понравился. Но вскоре я обнаружил, что ему сильно не хватало способности выполнять задачи с интенсивным использованием процессора. Итак, я начал поискать в Google и получил следующие ответы для решения проблемы: волокна, веб-воркеры и потоки (thread-a-gogo). Теперь, какой из них использовать, вызывает путаницу, и один из них определенно нужно использовать - в конце концов, какова цель иметь сервер, который просто хорош в вводе-выводе и ничего больше? Нужны предложения!

ОБНОВИТЬ:

Я подумывал о том, чтобы было поздно; просто нужны предложения по этому поводу. Теперь я подумал вот о чем: давайте создадим несколько потоков (используя thread_a_gogo или, возможно, веб-воркеров). Теперь, когда нам нужно их больше, мы можем создавать больше. Но процесс создания будет иметь некоторые ограничения. (не подразумевается системой, но, вероятно, из-за накладных расходов). Теперь, когда мы превысим лимит, мы можем разветвить новый узел и начать создавать потоки поверх него. Таким образом, это может продолжаться до тех пор, пока мы не достигнем определенного предела (в конце концов, процессы тоже имеют большие накладные расходы). Когда этот лимит достигнут, мы начинаем ставить задачи в очередь. Когда поток становится свободным, ему назначается новая задача. Таким образом, все может продолжаться гладко.

Вот об этом я и подумал. Это хорошая идея? Я немного новичок во всех этих процессах и потоках, поэтому не имею в этом никакого опыта. Поделитесь, пожалуйста, своим мнением.

Спасибо. :)


Обратите внимание: воркеры - это спецификация браузера, а не функция Javascript.
FredTheWebGuy

Что ж, я это вижу. Мой вопрос касался node.js - серверного кода, а не клиентской стороны!
Parth Thakkar

Просто пояснение - я вижу, что исходный вопрос был о веб-воркерах в NodeJs, что невозможно - NodeJs использует «потоки». Однако существует модуль NodeJS, который позволяет использовать синтаксис WebWorker в среде выполнения NodeJs.
FredTheWebGuy

Ответы:


331

У Node совершенно другая парадигма, и как только она будет правильно схвачена, будет легче увидеть этот другой способ решения проблем. Вам никогда не понадобится несколько потоков в приложении Node (1), потому что у вас есть другой способ сделать то же самое. Вы создаете несколько процессов; но это очень сильно отличается от, например, того, как это делает, например, Prefork mpm веб-сервера Apache.

А пока давайте подумаем, что у нас есть только одно ядро ​​процессора, и мы разработаем приложение (в стиле Node) для выполнения некоторой работы. Наша задача - побайтово обрабатывать большой файл с его содержимым. Лучший способ для нашей программы - начать работу с начала файла, побайтно до конца.

- Привет, Хасан, я полагаю, ты либо новичок, либо очень олдскульный со времен моего дедушки !!! Почему бы вам не создать несколько потоков и не сделать их намного быстрее?

- Ой, у нас только одно ядро ​​процессора.

-- Ну и что? Создайте несколько потоков, чувак, сделайте это быстрее!

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

- Хорошо, хорошо, я вижу, ты бедный. Воспользуемся моим компьютером, у него 32 ядра!

- Ух ты, мой дорогой друг, большое тебе спасибо. Я ценю его!

Затем вернемся к работе. Теперь у нас 32 ядра процессора благодаря нашему богатому другу. Правила, которые мы должны соблюдать, только что изменились. Теперь мы хотим использовать все это богатство, которое нам дано.

Чтобы использовать несколько ядер, нам нужно найти способ разделить нашу работу на части, которые мы можем обрабатывать параллельно. Если бы это был не Node, мы бы использовали для этого потоки; 32 потока, по одному на каждое ядро ​​процессора. Однако, поскольку у нас есть Node, мы создадим 32 процесса Node.

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

- Привет, Хасан, ты еще работаешь однопоточным? Что с тобой не так, мужик? Я только что предоставил вам то, что вы хотели. Тебе больше нет оправданий. Создавайте потоки, заставляйте их работать быстрее.

- Я разделил работу на части, и каждый процесс будет работать над одной из этих частей параллельно.

- Почему вы не создаете темы?

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

- Нет ладно, я крут, просто не понимаю, почему ты не используешь нитки?

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

- Хорошо, дайте мне знать, если вам понадобится другой компьютер. :п

Если я создам 33 процесса вместо 32, планировщик операционной системы будет приостанавливать поток, запускать другой, приостанавливать его после нескольких циклов, запускать другой снова ... Это ненужные накладные расходы. Я этого не хочу. Фактически, в системе с 32 ядрами я бы даже не хотел создавать ровно 32 процесса, 31 может быть лучше . Потому что в этой системе будет работать не только мое приложение. Было бы неплохо оставить немного места для других вещей, особенно если у нас 32 комнаты.

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

- Хм, Хасан, извини, что немного поиздевался над тобой. Думаю, теперь я понимаю вас лучше. Но есть еще кое-что, что мне нужно объяснить: что за шум о запуске сотен потоков? Я везде читал, что потоки создаются намного быстрее и тупее, чем процессы разветвления? Вы разветвляете процессы, а не потоки, и думаете, что это самый высокий уровень, который вы можете получить с помощью Node. Тогда разве Node не подходит для такой работы?

- Не волнуйся, я тоже крут. Все говорят такие вещи, так что я привык их слышать.

-- Так? Узел не подходит для этого?

- Node отлично подходит для этого, хотя потоки тоже могут быть хорошими. Что касается накладных расходов на создание потока / процесса; на том, что вы часто повторяете, на счету каждая миллисекунда. Однако я создаю всего 32 процесса, и это займет совсем немного времени. Произойдет это только один раз. Это не будет иметь никакого значения.

- Когда тогда я хочу создать тысячи потоков?

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

- Но ведь узел другой? Правильно?

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

- Хм, интересно; но вы можете запускать только одну функцию одновременно, если вы не используете несколько потоков. Как это может работать, если на веб-сервер одновременно поступает много запросов?

- Вы совершенно правы в том, что функции выполняются по одной, а не две параллельно. Я имею в виду, что в одном процессе одновременно выполняется только одна область кода. Планировщик ОС не приходит и не приостанавливает эту функцию и не переключается на другую, если только он не приостанавливает процесс, чтобы дать время другому процессу, а не другому потоку в нашем процессе. (2)

- Тогда как процесс может обрабатывать 2 запроса одновременно?

- Процесс может обрабатывать десятки тысяч запросов одновременно, если в нашей системе достаточно ресурсов (ОЗУ, Сеть и т. Д.). КЛЮЧЕВОЕ ОТЛИЧИЕ от того, как работают эти функции.

- Хм, а я должен волноваться?

- Может быть :) Узел запускает цикл по очереди. В этой очереди находятся наши задания, т. Е. Звонки, которые мы начали обрабатывать входящими запросами. Самым важным моментом здесь является то, как мы проектируем наши функции для работы. Вместо того, чтобы начинать обработку запроса и заставлять вызывающего абонента ждать, пока мы закончим работу, мы быстро завершаем нашу функцию после выполнения приемлемого объема работы. Когда мы подходим к точке, где нам нужно дождаться, пока другой компонент выполнит некоторую работу и вернет нам значение, вместо того, чтобы ждать этого, мы просто завершаем нашу функцию, добавляя остальную работу в очередь.

- Это звучит слишком сложно?

- Нет-нет, это может показаться сложным; но сама система очень проста и имеет смысл.

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

Таким образом, мы делаем то, что обычно делает планировщик ОС. В какой-то момент мы приостанавливаем нашу работу и позволяем другим вызовам функций (например, другим потокам в многопоточной среде) выполняться, пока снова не дойдем до нашей очереди. Это намного лучше, чем оставлять работу планировщику ОС, который пытается уделить время каждому потоку в системе. Мы знаем, что делаем, намного лучше, чем OS Scheduler, и ожидается, что мы остановимся, когда нам следует остановиться.

Ниже приведен простой пример, в котором мы открываем файл и читаем его, чтобы поработать с данными.

Синхронный способ:

Open File
Repeat This:    
    Read Some
    Do the work

Асинхронный способ:

Open File and Do this when it is ready: // Our function returns
    Repeat this:
        Read Some and when it is ready: // Returns again
            Do some work

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

Таким образом, у Node совершенно другая парадигма, чем у многопоточной разработки; но это не значит, что в нем чего-то не хватает. Для синхронного задания (где мы можем определить порядок и способ обработки) он работает так же, как и многопоточный параллелизм. Для работы, которая приходит извне, например, запросов к серверу, она просто лучше.


(1) Если вы не создаете библиотеки на других языках, таких как C / C ++, и в этом случае вы все равно не создаете потоки для разделения заданий. Для такого рода работы у вас есть два потока, один из которых будет продолжать связь с Node, а другой будет выполнять реальную работу.

(2) Фактически, каждый процесс Node имеет несколько потоков по тем же причинам, которые я упомянул в первой сноске. Однако это не похоже на то, чтобы 1000 потоков выполняли аналогичные работы. Эти дополнительные потоки предназначены для таких вещей, как прием событий ввода-вывода и обработка межпроцессного обмена сообщениями.

ОБНОВЛЕНИЕ (как ответ на хороший вопрос в комментариях)

@Mark, спасибо за конструктивную критику. В парадигме Node у вас никогда не должно быть функций, обработка которых занимает слишком много времени, если только все остальные вызовы в очереди не предназначены для выполнения один за другим. В случае задач, требующих больших вычислительных ресурсов, если мы посмотрим на картину целиком, мы увидим, что это не вопрос «Следует ли нам использовать потоки или процессы?» но возникает вопрос: «Как мы можем сбалансированно разделить эти задачи на подзадачи, чтобы мы могли запускать их параллельно, используя несколько ядер ЦП в системе?» Допустим, мы обработаем 400 видеофайлов в системе с 8 ядрами. Если мы хотим обрабатывать один файл за раз, то нам нужна система, которая будет обрабатывать разные части одного и того же файла, и в этом случае, возможно, многопоточную однопроцессную систему будет проще построить и даже более эффективно. Мы все еще можем использовать Node для этого, запустив несколько процессов и передавая сообщения между ними, когда необходимо совместное использование состояния / связь. Как я уже сказал ранее, многопроцессорный подход с Nodeа также многопоточность в такого рода задачах; но не более того. Опять же, как я уже говорил ранее, ситуация, в которой Node сияет, - это когда у нас есть эти задачи, поступающие в систему в качестве входных данных из нескольких источников, поскольку одновременное поддержание большого количества соединений в Node намного легче по сравнению с потоком на соединение или процессом на соединение. система.

Что касается setTimeout(...,0)звонков; иногда может потребоваться перерыв во время выполнения трудоемкой задачи, чтобы позволить вызовам в очереди иметь свою долю обработки. Разделение задач по-разному может спасти вас от этого; но, тем не менее, это не совсем взлом, это просто порядок работы очереди событий. Кроме того, использование process.nextTickдля этой цели намного лучше, поскольку при использовании setTimeoutбудут необходимы подсчет и проверка прошедшего времени, в то время как process.nextTickэто просто то, что мы действительно хотим: «Эй, задача, вернись в конец очереди, вы использовали свою долю! "


9
Удивительный! Чертовски круто! Мне понравилось, как вы ответили на этот вопрос! :)
Parth Thakkar 01

48
Конечно :) Я действительно не могу поверить, что есть очень злые люди, которые голосуют против этой статьи с ответами! Опрашивающий называет это "Чертовски удивительно!" и автор книги предлагает мне написать на своем веб-сайте, увидев это; но некоторые гении проголосовали против. Почему бы вам не поделиться своими яркими интеллектуальными качествами и не прокомментировать это вместо того, чтобы подло и украдкой отрицать, а? Почему что-то хорошее так вас беспокоит? Почему вы хотите, чтобы что-то полезное не добралось до других людей, которые действительно могут извлечь из этого пользу?
hasanyasin 01

9
Это не совсем честный ответ. А как насчет дорогостоящих в вычислительном отношении задач, в которых мы не можем «быстро завершить» вызов функции? Я считаю, что некоторые люди используют setTimeout(...,0)для этого какие-то хаки, но использование отдельной ветки в этом сценарии наверняка будет лучше?
mpen 07

3
@hasanyasin Это самое красивое объяснение узла, которое я нашел до сих пор! :)
Венемо

7
@Mark Как правило, если это так дорого с вычислительной точки зрения, есть опции / модули для рабочих процессов / рабочих процессов ... В общем, для таких вещей я использую очередь сообщений и рабочие процессы, которые обрабатывают задачу в время из очереди, и работайте над этой задачей. Это также позволяет масштабировать до нескольких серверов. В соответствии с этим в Substack есть множество модулей, предназначенных для подготовки и масштабирования, которые вы можете просмотреть.
Tracker1

34

(Обновление 2016: веб-сотрудники переходят на io.js - форк Node.js Node.js v7 - см. Ниже.)

(Обновление 2017: веб-воркеры не переходят на Node.js v7 или v8 - см. Ниже.)

(Update 2018: веб - работники будут идти в Node.js Node v10.5.0 - см . Ниже)

Некоторые пояснения

Прочитав ответы выше, я хотел бы отметить, что в веб-воркерах нет ничего, что противоречило бы философии JavaScript в целом и Node в частности в отношении параллелизма. (Если бы это было так, WHATWG даже не обсуждала бы это, не говоря уже о реализации в браузерах).

Вы можете думать о веб-воркере как о легком микросервисе, доступ к которому осуществляется асинхронно. Нет общего состояния. Проблем с блокировкой нет. Блокировки нет. Синхронизация не требуется. Так же, как когда вы используете службу RESTful из своей программы Node, вы не беспокоитесь о том, что она теперь «многопоточная», потому что служба RESTful не находится в том же потоке, что и ваш собственный цикл обработки событий. Это просто отдельная служба, к которой вы обращаетесь асинхронно, и это главное.

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

На самом деле веб-воркеры концептуально идеально подходят для Node, который - о чем многие люди не знают - кстати, довольно интенсивно использует потоки, и на самом деле «все работает параллельно, кроме вашего кода» - см.:

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

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

Часто рекомендуется разделить длительные операции, связанные с процессором, на более мелкие задачи (что-то вроде примера в разделе «Исходный ответ» моего ответа на Ускорение setInterval ), но это не всегда практично и не использует больше чем одно ядро ​​процессора.

Я пишу это, чтобы прояснить комментарии, которые в основном говорили, что веб-воркеры были созданы для браузеров, а не серверов (забывая, что это можно сказать почти обо всем в JavaScript).

Узловые модули

Есть несколько модулей, которые должны добавлять веб-воркеров в Node:

Я не использовал ни один из них, но у меня есть два быстрых наблюдения, которые могут иметь значение: по состоянию на март 2015 года node-webworker последний раз обновлялся 4 года назад, а node-webworker-thread последний раз обновлялся месяц назад. Также я вижу в примере использования node-webworker-threads, что вы можете использовать функцию вместо имени файла в качестве аргумента для конструктора Worker, который, кажется, может вызвать тонкие проблемы, если он реализован с использованием потоков, разделяющих память (если только functions используется только для его метода .toString () и в противном случае компилируется в другой среде, и в этом случае это может быть нормально - мне нужно более глубоко изучить его, просто поделившись своими наблюдениями здесь).

Если есть какой-либо другой соответствующий проект, который реализует API веб-работников в Node, оставьте комментарий.

Обновление 1

Я еще не знал этого на момент написания, но, кстати, за день до того, как я написал этот ответ, в io.js были добавлены веб-воркеры .

( io.js - это форк Node.js - подробнее см .: Почему io.js решил форкнуть Node.js , интервью InfoWorld с Майком Роджерсом).

Это не только доказывает, что в веб-воркерах нет ничего, что противоречило бы философии JavaScript в целом и Node в частности в отношении параллелизма, но и может привести к тому, что веб-воркеры станут первоклассными специалистами в серверном JavaScript, таком как io. js (и, возможно, в будущем Node.js) так же, как он уже присутствует в клиентском JavaScript во всех современных браузерах .

Обновление 2

В обновлении 1 и в моем твите я имел в виду запрос на перенос io.js № 1159, который теперь перенаправляет на узел PR № 1159, который был закрыт 8 июля и заменен узлом PR № 2133, который все еще открыт. В рамках этих запросов на вытягивание проходит некоторое обсуждение, которое может предоставить более свежую информацию о статусе Web worker в io.js / Node.js.

Обновление 3

Последняя информация - спасибо NiCk Newman за то, что разместил ее в комментариях: Есть коммит рабочих: начальная реализация, сделанный Петкой Антоновым от 6 сентября 2015 года, который можно загрузить и опробовать в этом дереве . См. Комментарии NiCk Newman для подробностей.

Обновление 4

По состоянию на май 2016 года последние комментарии к все еще открытому PR № 2133 - рабочие: первоначальная реализация были сделаны 3 месяца назад. 30 мая Матеус Морейра попросил меня опубликовать обновление этого ответа в комментариях ниже, и он спросил о текущем статусе этой функции в комментариях по связям с общественностью.

Первые ответы в PR-дискуссии были скептическими, но позже Бен Нордхейс написал, что «объединение этого в той или иной форме входит в мой список задач для v7».

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

Спасибо Matheus Moreira за указание на это в комментариях и за возобновление обсуждения на GitHub.

Обновление 5

По состоянию на июль 2016 года в npm есть несколько модулей, которые были недоступны раньше - для получения полного списка соответствующих модулей выполните поиск в npm для рабочих, веб-работников и т. Д. Если что-то конкретное работает или не работает для вас, отправьте комментарий.

Обновление 6

По состоянию на январь 2017 г. маловероятно, что веб-воркеры будут объединены в Node.js.

Запрос на вытягивание № 2133 рабочих: первоначальная реализация Петкой Антоновым от 8 июля 2015 года была окончательно закрыта Беном Нордхусом 11 декабря 2016 года, который прокомментировал, что «поддержка многопоточности добавляет слишком много новых режимов отказа, что не дает достаточных преимуществ» и «мы также можно добиться этого, используя более традиционные средства, такие как разделяемая память и более эффективная сериализация ».

Для получения дополнительной информации см. Комментарии к PR 2133 на GitHub.

Еще раз спасибо Matheus Moreira за указание на это в комментариях.

Обновление 6

Я рад сообщить, что несколько дней назад, в июне 2018 года, веб-воркеры появились в Node v10.5.0 в качестве экспериментальной функции, активируемой с помощью --experimental-workerфлага.

Для получения дополнительной информации см .:

🎉🎉🎉 Наконец-то! Я могу сделать седьмое обновление своего 3-летнего ответа на переполнение стека, в котором я утверждаю, что потоки а-ля веб-воркеры не противоречат философии Node, только на этот раз я говорю, что мы, наконец, получили это! 😜👍


1
@NiCkNewman Спасибо. Я вижу, что исходный запрос на вытягивание в io.js теперь закрыт и заменен другим - с некоторыми обсуждениями в комментариях к запросам на перенос на GitHub, возможно, вы сможете найти там некоторую информацию. Смотрите: Обновление 2 в моем ответе.
rsp

1
Да, похоже, они только что исправили последнюю проблему с libuv. Интересно, когда я смогу достать модуль. Не могу дождаться! Спасибо за то, что держите нас в курсе ~ Edit: только что инициализирован: github.com/petkaantonov/io.js/commit/ ... Вот и все!
NiCk Newman

1
Ага, это в прямом эфире. (Еще не реализовано официально), но вы можете скачать исходный код здесь: github.com/petkaantonov/io.js/tree/… и скомпилировать, если хотите протестировать! Я делаю это сейчас ~
Никк Ньюман

1
@NiCkNewman Спасибо за новую информацию - добавил в ответ.
rsp

1
Не могли бы вы сообщить нам о статусе реализации Node.js workers? Последние комментарии в PR # 2133 от февраля; разработчики явно столкнулись с проблемой, и нет никаких комментариев, свидетельствующих о ее решении.
Матеус Морейра

8

Я пришел из старой школы мысли, когда мы использовали многопоточность, чтобы сделать программное обеспечение быстрым. Последние 3 года я использую Node.js и очень его поддерживаю. Как hasanyasin подробно объяснил, как работает узел, и концепцию асинхронной функциональности. Но позвольте мне добавить сюда несколько вещей.

В прежние времена с одноядерными процессорами и более низкими тактовыми частотами мы пробовали различные способы заставить программное обеспечение работать быстро и параллельно. в дни DOS мы запускали одну программу за раз. Затем в Windows мы начали запускать несколько приложений (процессов) вместе. Были проверены такие концепции, как упреждающий и не упреждающий (или кооперативный). Теперь мы знаем, что вытеснение было ответом на улучшение многопроцессорной задачи на одноядерных компьютерах. Затем появились концепции процессов / задач и переключения контекста. Чем концепция потока, чтобы еще больше снизить нагрузку на переключение контекста процесса. Поток был придуман как легкая альтернатива порождению новых процессов.

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

Nodejs - это единый процесс, обеспечивающий асинхронный механизм. Здесь задания отправляются в нижележащую ОС для выполнения задач, пока мы ожидаем в цикле событий завершения задачи. Как только мы получаем зеленый сигнал от ОС, мы выполняем все, что нам нужно. В некотором смысле это совместная / не вытесняющая многозадачность, поэтому мы никогда не должны блокировать цикл событий на очень длительный период времени, иначе мы очень быстро ухудшим наше приложение.
Так что, если когда-либо возникнет задача, которая является блокирующей по своей природе или отнимает очень много времени, нам придется разделить ее на вытесняющий мир ОС и потоков. хорошие примеры этого есть в документации libuv . Кроме того, если вы читаете документацию далее вы обнаружите , что FileI / O обрабатывается в потоках в node.js .

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

Под капотом в node.js все его c ++ и потоки. Кроме того, node предоставляет на С ++ способ расширения своей функциональности и дальнейшего ускорения за счет использования потоков там, где они необходимы, то есть блокирования таких задач, как чтение из источника, запись в источник, анализ больших данных и т. Д.

Я знаю, что ответ hasanyasin - принятый, но для меня потоки будут существовать независимо от того, что вы говорите или как вы скрываете их за скриптами, во-вторых, никто просто не разбивает вещи на потоки только для скорости, это в основном делается для блокировки задач. А потоки находятся в основе Node.js, поэтому до полного отказа многопоточность правильная. Кроме того, потоки отличаются от процессов, и ограничение наличия узловых процессов на ядро ​​точно не относится к количеству потоков, потоки похожи на подзадачи для процесса. на самом деле потоки не отображаются в диспетчере задач Windows или в верхней команде Linux. еще раз они меньше веса чем обрабатывает


Асинхронный код - это не какое-то серьезное новшество (на самом деле, он у нас был десятилетиями), и многопоточность - это не какая-то устаревшая технология, которую нужно заменить. Это разные инструменты с разными компромиссами, и на самом деле их даже можно довольно хорошо комбинировать. Каждый раз, когда вы запускаете кластер узлов, вы фактически запускаете несколько «потоков» (в данном случае процессы, но то же самое можно было бы достичь с помощью потоков, и это было бы еще более легким). Или возьмем Erlang или Go, которые могут запускать тысячи зеленых потоков ...
Hejazzman

Я думаю, что главный момент, который нам не хватает, заключается в том, что процесс в ОС всегда будет выполняться упреждающим образом для обеспечения справедливости. Также с несколькими процессорами вы можете иметь фактическое выполнение параллельного кода, но даже тогда у вас будет приоритетное прерывание. Асинхронная работа также выполняется ОС в некоторых процессах.
limplash

4

Я не уверен, актуальны ли веб-работники в этом случае, они являются клиентскими технологиями (запускаются в браузере), а node.js запускается на сервере. Волокна, насколько я понимаю, также являются блокирующими, т.е. они добровольно выполняют многозадачность, поэтому вы можете их использовать, но сами должны управлять переключением контекста yield. На самом деле потоки могут быть тем, что вам нужно, но я не знаю, насколько они зрелы в node.js.


3
просто для вашей информации, веб-воркеры были (частично) адаптированы на node.js. И доступны как node-workersпакет. Взгляните на это: github.com/cramforce/node-worker
Парт Таккар

Полезно знать, спасибо. Документов очень мало, я не знаю, работает ли он в отдельном потоке, процессе или просто в одном процессе, и у меня нет времени копаться в коде, поэтому я понятия не имею, будет ли он работать для вашего случая.
lanzz

@ParthThakkar: Этот проект не затрагивался 3 года (2 года, когда вы публиковали), и он не прошел через 0.0.1.
mpen 07

@Mark: Причина моего незнания в том, что я еще не профессиональный программист. Черт возьми, я даже не в университете. Я до сих пор учусь в старшей школе и продолжаю читать о программировании - помимо управления школьной работой. Так что у меня нет возможности знать обо всех подобных вопросах. Я только что опубликовал то, что знал ...
Парт Таккар

@Mark: Хотя было мило с вашей стороны упомянуть об истории проекта. О таких вещах я позабочусь в будущих ответах !! :)
Parth Thakkar

3

worker_threadsбыл реализован и отправлен под флагом в node@10.5.0. Это все еще первоначальная реализация, и необходимы дополнительные усилия, чтобы сделать ее более эффективной в будущих выпусках. Стоит попробовать в последнюю очередь node.


2

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

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

Для работы с асинхронным программированием в вашем коде (например, избегая вложенных пирамид обратного вызова) компонент [Future] в библиотеке Fibers является достойным выбором. Я также предлагаю вам проверить Asyncblock, основанный на Fibers. Волокна хороши тем, что позволяют скрыть обратный вызов, дублируя стек, а затем переходя между стеками в однопоточном режиме по мере необходимости. Избавляет вас от хлопот с настоящими потоками, давая вам преимущества. Обратной стороной является то, что трассировки стека могут быть немного странными при использовании волокон, но они не так уж и плохи.

Если вам не нужно беспокоиться об асинхронных материалах и вы больше просто заинтересованы в выполнении большого объема обработки без блокировки, простой вызов process.nextTick (обратный вызов) время от времени - это все, что вам нужно.


Ну, ваше предложение - о кластерах - было тем, о чем я изначально думал. Но проблема заключается в их накладных расходах - новый экземпляр v8 должен инициализироваться каждый раз, когда новый процесс разветвляется (~ 30 мс, 10 МБ). Итак, вы не можете создать их много. Это взято непосредственно из документации узла: эти дочерние узлы (о child_processes) все еще являются совершенно новыми экземплярами V8. Предположим, что минимум 30 мс при запуске и 10 МБ памяти для каждого нового узла. То есть вы не можете создать их много тысяч.
Парт Таккар

1
В этом и заключается идея кластера. Вы запускаете одного рабочего на ядро ​​процессора. Больше в этом, скорее всего, нет необходимости. Даже задачи с интенсивным использованием ЦП будут работать в асинхронном стиле. Однако, если вам действительно нужны полнофункциональные потоки, вам, вероятно, следует подумать о полном переходе на другой серверный бэкэнд.
genericdave

1

Возможно, вам поможет дополнительная информация о том, какие задачи вы выполняете. Почему вам нужно (как вы упомянули в своем комментарии к ответу genericdave) создавать многие тысячи из них? Обычный способ сделать это в Node - запустить рабочий процесс (с помощью fork или какого-либо другого метода), который всегда выполняется и может передаваться с помощью сообщений. Другими словами, не запускайте нового воркера каждый раз, когда вам нужно выполнить любую задачу, которую он выполняет, а просто отправьте сообщение уже запущенному воркеру и получите ответ, когда он будет выполнен. Честно говоря, я не вижу, чтобы запуск многих тысяч реальных потоков был бы очень эффективным, вы по-прежнему ограничены вашими процессорами.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.