Вузол має зовсім іншу парадигму, і як тільки він буде правильно зафіксований, легше побачити цей різний спосіб вирішення проблем. У додатку Node (1) вам ніколи не потрібно кілька потоків, оскільки у вас є інший спосіб робити те саме. Ви створюєте кілька процесів; але це дуже сильно відрізняється, ніж, наприклад, як працює Prefork mpm веб-сервера Apache.
Поки давайте подумаємо, що у нас є лише одне ядро процесора, і ми розробимо додаток (способом Node), щоб виконати певну роботу. Наша робота - обробити великий файл, що працює над його вмістом, байт-байт. Найкращий спосіб для нашого програмного забезпечення - почати роботу з початку файлу, дотримуватися його байт-байтом до кінця.
- Гей, Хасане, я гадаю, ти або новачок, або дуже стара школа з часів мого Діда !!! Чому б вам не створити деякі теми і зробити це набагато швидше?
- О, у нас є лише одне ядро процесора.
-- І що? Створіть людину нитки, зробіть це швидше!
- Це не працює так. Якщо я створю теми, я буду робити це повільніше. Тому що я буду додавати багато накладних витрат до системи для перемикання між потоками, намагаючись приділити їм достатньо часу, і всередині свого процесу намагаюся спілкуватися між цими потоками. Окрім усіх цих фактів, мені також доведеться подумати про те, як я поділяю одну роботу на кілька частин, які можна зробити паралельно.
- Гаразд добре, я бачу, ти бідний. Давайте використовуємо мій комп'ютер, він має 32 ядра!
- Нічого собі, ти чудовий мій дорогий друг, дуже дякую. Я ціную це!
Потім повертаємось до роботи. Зараз у нас є 32 ядра процесора завдяки нашому багатому другу. Правила, яких ми повинні дотримуватися, тільки що змінилися. Тепер ми хочемо використати все це багатство, яке нам дано.
Для використання декількох ядер нам потрібно знайти спосіб розділити свою роботу на шматки, з якими ми можемо оброблятись паралельно. Якби не Вузол, ми використовували б для цього потоки; 32 нитки, по одній для кожного ядра процесора. Однак, оскільки у нас є Node, ми створимо 32 процеси у Node.
Нитки можуть бути хорошою альтернативою процесам Node, можливо, навіть кращим способом; але лише в конкретному виді роботи, де робота вже визначена і ми маємо повний контроль над тим, як впоратися з нею. Окрім цього, для будь-якого іншого виду проблем, коли робота надходить ззовні таким чином, щоб ми не мали контролю над цим, і ми хочемо відповісти якнайшвидше, спосіб Вузла є безперечно кращим.
- Гей, Хасане, ти все ще працюєш однонитковим? Що з тобою, чоловіче? Я щойно надав тобі те, що ти хотів. У вас більше немає виправдань. Створіть теми, змусьте її працювати швидше.
- Я розділив роботу на частини, і кожен процес буде працювати над однією з цих частин паралельно.
- Чому б вам не створити теми?
- Вибачте, я не думаю, що це корисно. Ви можете взяти комп’ютер, якщо хочете?
- Ні гаразд, я крутий, я просто не розумію, чому ви не використовуєте теми?
- Дякую за комп’ютер. :) Я вже розділив роботу на шматки і створюю процеси для роботи над цими творами паралельно. Всі ядра процесора будуть повністю використані. Я міг би це зробити з потоками замість процесів; але у Вузла є такий шлях, і мій бос Парт Тхакар хоче, щоб я використовував Вузло.
- Гаразд, дайте мені знати, якщо вам потрібен інший комп’ютер. : с
Якщо я створять 33 процеси, а не 32, планувальник операційної системи буде призупиняти потік, запускати інший, робити паузу після деяких циклів, запускати інший ще раз ... Це зайве накладні витрати. Я не хочу цього. Насправді в системі з 32 ядрами я навіть не хотів би створити рівно 32 процеси, 31 може бути приємніше . Тому що не тільки мій додаток буде працювати в цій системі. Залишити трохи місця для інших речей може бути добре, особливо якщо у нас 32 кімнати.
Я вважаю, що ми зараз знаходимося на одній сторінці про повне використання процесорів для завдань, що вимагають процесора .
- Хм, Хасане, мені шкода, що трохи знущався над тобою. Я вірю, що зараз я тебе краще розумію. Але все ж є щось, для чого мені потрібно пояснення: у чому вся суєта щодо запуску сотень потоків? Я читаю всюди, що нитки набагато швидше створюються і тупіші, ніж розгортання процесів? Ви розкручуєте процеси замість ниток, і вважаєте, що це найвищий рівень, який ви отримаєте з Node. Тоді чи Node не підходить для такої роботи?
- Не хвилюйтесь, я теж крутий. Усі говорять про це, тому я думаю, що я звик їх чути.
-- Так? Вузол для цього не годиться?
- Вузол ідеально підходить для цього, навіть якщо нитки можуть бути хорошими. Що стосується створення потоку / процесу накладних витрат; на речі, які ви багато повторюєте, кожна мілісекунда рахується. Однак я створюю лише 32 процеси, і це займе небагато часу. Це станеться лише один раз. Це не матиме ніякого значення.
- Коли тоді я хочу створити тисячі ниток?
- Ти ніколи не хочеш створювати тисячі ниток. Однак у системі, яка виконує роботу, яка надходить ззовні, як веб-сервер, що обробляє HTTP-запити; якщо ви використовуєте потік для кожного запиту, ви будете створювати безліч потоків, багато з них.
- Хоч вузол інший? Правильно?
- Так, саме. Тут справді світить Вузол. Як і потік набагато легший, ніж процес, виклик функції набагато легший, ніж потік. Вузол викликає функції замість створення потоків. У прикладі веб-сервера кожен вхідний запит викликає виклик функції.
- Хм, цікаво; але ви можете запускати одну функцію одночасно, лише якщо ви не використовуєте декілька потоків. Як це може працювати, коли багато веб-запитів надходить одночасно на веб-сервер?
- Ви абсолютно праві, як функціонують функції, одна за одною, ніколи дві паралельно. Я маю на увазі, що в одному процесі одночасно працює лише одна область коду. Планувальник ОС не приходить і призупиняє цю функцію та переходить на іншу, якщо тільки вона не призупиняє процес, щоб приділити час іншому процесу, а не іншому потоку в нашому процесі. (2)
- Тоді як процес може обробляти 2 запити одночасно?
- Процес може обробляти десятки тисяч запитів одночасно, якщо наша система має достатньо ресурсів (оперативна пам'ять, мережа тощо). КІЛЬКА РОЗВИТКУ цих функцій.
- Хм, чи варто мені зараз хвилюватись?
- Можливо :) Вузол проводить цикл над чергою. У цій черзі є наші завдання, тобто дзвінки, які ми почали обробляти вхідні запити. Найважливішим моментом тут є те, як ми проектуємо наші функції для виконання. Замість того, щоб починати обробляти запит і змушувати абонента чекати, поки ми закінчимо роботу, ми швидко припиняємо свою функцію після виконання прийнятного обсягу роботи. Коли ми доходимо до того, коли нам потрібно чекати, коли інший компонент виконає якусь роботу і поверне нам значення, а не чекати цього, ми просто закінчимо свою функцію, додавши решту роботи до черги.
- Це звучить занадто складно?
- Ні ні, я можу звучати складно; але сама система дуже проста і має ідеальний сенс.
Тепер я хочу перестати цитувати діалог між цими двома розробниками і закінчити свою відповідь після останнього короткого прикладу того, як ці функції працюють.
Таким чином ми робимо те, що зазвичай планував ОС. Ми зупиняємо свою роботу в якийсь момент і дозволяємо виконувати інші виклики функцій (як і інші потоки в багатопотоковому середовищі), поки ми знову не повернемося. Це набагато краще, ніж залишити роботу планувальнику ОС, який намагається приділити достатньо часу кожному потоку в системі. Ми знаємо, що робимо набагато краще, ніж робить Scheduler OS, і, як очікується, зупинимось, коли нам слід зупинитися.
Нижче наводиться простий приклад, коли ми відкриваємо файл і читаємо його, щоб виконати певну роботу над даними.
Синхронний шлях:
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) Насправді кожен процес у вузлі має декілька потоків з тих же причин, про які я згадував у першій виносці. Однак це зовсім не так, як 1000 ниток роблять подібні роботи. Ці додаткові потоки призначені для таких речей, як прийняття подій вводу-виводу та обробка міжпроцесорних повідомлень.
ОНОВЛЕННЯ (як відповідь на гарне запитання в коментарях)
@ Марк, дякую за конструктивну критику. У парадигмі Node у вас ніколи не повинно бути функцій, які займають занадто багато часу, якщо всі інші виклики в черзі не розроблені для запуску один за одним. У випадку обчислювально дорогих завдань, якщо ми дивимось картину в повному обсязі, ми бачимо, що це не питання "Чи слід використовувати нитки чи процеси?" але питання "Як можна добре розподілити ці завдання на підзадачі, щоб ми могли їх виконувати паралельно, використовуючи кілька ядер процесора в системі?" Скажімо, ми обробимо 400 відеофайлів у системі з 8 ядрами. Якщо ми хочемо обробляти один файл за один раз, тоді нам потрібна система, яка буде обробляти різні частини одного і того ж файлу, і, можливо, багатопотокова однопроцесова система буде легше побудувати та ще ефективніше. Ми все ще можемо використовувати для цього Node, запускаючи кілька процесів і передаючи повідомлення між ними, коли необхідний обмін державою / спілкування. Як я вже говорив раніше, багатопроцесорний підхід з Node єа також багатопотоковий підхід у подібних завданнях; але не більше того. Знову ж таки, як я вже говорив раніше, ситуація, що Node світиться, полягає в тому, що у нас ці завдання надходять як вхід до системи з декількох джерел, оскільки збереження багатьох з'єднань одночасно набагато легше у Node порівняно з потоком на з'єднання або процесом на з'єднання система.
Що стосується setTimeout(...,0)
дзвінків; іноді може бути потрібна перерва під час трудомісткого завдання, щоб дозволити дзвінки в черзі мати свою частку обробки. Поділ завдань різними способами може врятувати вас від цього; але все-таки це насправді не хак, це лише спосіб роботи черг на події. Крім того, використовувати process.nextTick
для цієї мети набагато краще, оскільки коли ви використовуєте setTimeout
, обчислення та перевірку пройденого часу знадобиться, тоді process.nextTick
як ми просто хочемо: "Ей, завдання, поверніться до кінця черги, ви використали свою частку! "