Является ли NodeJs однопоточным?
Node.js запускает код JavaScript в одном потоке, что означает, что ваш код может выполнять только одну задачу за раз. Но сам Node.js является многопоточным и предоставляет скрытые потоки через библиотеку `libuv`, которая обрабатывает операции ввода-вывода, такие как чтение файлов с диска или сетевые запросы. Благодаря использованию скрытых потоков Node.js предоставляет асинхронные методы, которые позволяют вашему коду выполнять запросы ввода-вывода, не блокируя основной поток.
Node.js представил нам модуль рабочих потоков. Этот модуль позволяет создавать потоки и выполнять задачи Javascript параллельно. Если поток завершает задачу, он отправляет сообщение в основной поток, содержащее результат, чтобы мы могли использовать его в основном потоке. Рабочие потоки не блокируют основной поток, поэтому мы можем разделить и распределить нашу задачу на несколько частей, чтобы оптимизировать ее.
Давайте сделаем это на примере и посмотрим на него поближе.
Создаем проект Nodejs и устанавливаем Express framework
mkdir multiple-thread
cd multiple-thread
npm init -y
npm install express
Прежде всего, это процессы и потоки. Мы должны знать различия между ними.
Что такое процесс?
Процесс запускает программу в ОС. Он может выполнять одну задачу за раз.
Давайте рассмотрим процесс поближе. Создайте файл JS и зарегистрируйте процесс.
//ex-process.js
const processName = process.argv.slice(2)[0]
let count = 0;
while(true){
count++;
if(count === 3333 || count=== 6666){
console.log(`${processName}: ${count}`)
}
}
node ex-process.js FirstEx &
// & symbol means the Node program to run in the background, so we can use
our teminal for other works
Output
[1] 82942
FirstEx: 3333
FirstEx: 6666
ps |grep node
Output
82942 ttys001 2:38.85 node ex-process.js FirstEx
83105 ttys001 0:00.00 grep node
Потоки — это относительно легкий способ реализации нескольких путей выполнения внутри приложения. На системном уровне программы выполняются параллельно, при этом система распределяет время выполнения каждой программы в зависимости от ее потребностей и потребностей других программ.
Давайте закодируем Express Server с неблокирующим маршрутом и блокирующими маршрутами.
Давайте напишем обычный блокирующий маршрут для пользователей. В app.js;
const express = require('express');
const app = express();
app.get('/hello', (req,res)=>{
res.status(200).send(`Hello`);
})
app.get('/blocking-route', (req,res)=>{
console.log('Blocking route called')
let counter = 0;
for (let i = 0; i < 15_000_000_000; i++) {
counter++;
}
res.status(200).send(`result is ${counter}`);
})
app.listen(7333, ()=> console.log('Server started on port 7333'));
Он не отвечает, пока blocking-routeне закончит и не отправит ответ.
Почему он не ответил нам мгновенно?
В этом примере цикл for с привязкой к процессору блокирует основной поток. Пока основной поток заблокирован, Node.js не может обрабатывать какие-либо запросы, пока не завершится процесс, привязанный к ЦП. поэтому ни один из других маршрутов не /helloработает при /blocking-routeзагрузке маршрута.
Таким образом, если у нас есть API с тысячами одновременных GETзапросов к неблокирующему маршруту, например /hello, только один запрос заблокирует /blocking-routeвсе маршруты в API. Это означает, что все маршруты приложений не отвечают.
JavaScript DAAA IT, мы не можем использовать другой поток
Dudeeeee Это не только JS. Это Нодейс. Мы можем разгрузить задачу, связанную с процессором, с помощью рабочих потоков.
Прежде всего, мы должны проверить наши ядра с помощью nprocкоманды
nproc
output
8
Давайте создадим еще один JS-файл, например worker.js, имя на ваше усмотрение. В этом файле мы выполняем задачу с интенсивным использованием процессора
//worker.js
const { parentPort } = require("worker_threads");
let counter = 0;
console.log("Worker started");
for (let i = 0; i < 15_000_000_000; i++) {
counter++;
}
parentPort.postMessage(counter);
//app.js
const express = require("express");
const { Worker } = require("worker_threads");
const app = express();
app.get("/blocking-route", async (req, res) => {
console.log("Blocking route called");
let counter = 0;
for (let i = 0; i < 15_000_000_000; i++) {
counter++;
}
res.status(200).send(`result is ${counter}`);
});
app.get("/hello", (req, res) => {
res.status(200).send(`Hello`);
});
app.get("/non-blocking-route", async (req, res) => {
console.log("Non-blocking route called");
const worker = new Worker("./worker.js");
worker.on("message", (data) => {
res.status(200).send(`result is ${data}`);
});
worker.on("error", (err) => {
res.status(404).send(` error ${err}`);
});
console.log("I am not blocked");
});
app.listen(7333, () => console.log("Server started on port 7333"));
Запустим сервер с node app.js. Откройте браузер и перейдите, http://localhost:7333/non-blocking-routeпрежде чем он вернет ответ http://localhost:7333/hello. Вы поняли это? Получаем ответ моментально. Блокировки нет. Мы не стали ждать, пока другие маршруты закончат загрузку. Потому что задача, привязанная к ЦП, разгружается и работает в другом потоке, а основной поток обрабатывает все запросы. Мы можем использовать больше работников для повышения нашей производительности. Например, у меня 8 ядер и я могу разделить свой Процесс на 8.
Заключение
Рабочие процессы (потоки) могут помочь с операциями JavaScript, интенсивно использующими ЦП. В отличие от child_processили cluster, worker_threadsможет совместно использовать память.
В этой статье я попытался упростить задачу и объяснить, как работает worker_threads?
Спасибо за прочтение

![В любом случае, что такое связанный список? [Часть 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































