Czy NodeJs jest jednowątkowy?

Mar 20 2023
Jak pracować z innymi wątkami w Nodejs?
Node.js uruchamia kod JavaScript w jednym wątku, co oznacza, że ​​Twój kod może wykonywać tylko jedno zadanie na raz.
Proces I/O Nodejsa

Node.js uruchamia kod JavaScript w jednym wątku, co oznacza, że ​​Twój kod może wykonywać tylko jedno zadanie na raz. Ale sam Node.js jest wielowątkowy i zapewnia ukryte wątki poprzez bibliotekę `libuv`, która obsługuje operacje wejścia/wyjścia, takie jak odczyt plików z dysku lub żądania sieciowe. Dzięki wykorzystaniu ukrytych wątków Node.js udostępnia metody asynchroniczne, które pozwalają Twojemu kodowi wykonywać żądania we/wy bez blokowania głównego wątku.

Node.js przedstawił nam moduł worker-threads. Moduł ten umożliwia równoległe tworzenie wątków i wykonywanie zadań Javascript. Jeśli wątek zakończy zadanie, wysyła wiadomość do głównego wątku zawierającą wynik, abyśmy mogli go użyć w głównym wątku. Wątki robocze nie blokują głównego wątku, więc możemy podzielić i rozdzielić nasze zadanie na wiele części, aby je zoptymalizować.

Zróbmy z tego przykład i przyjrzyjmy się mu bliżej.

Tworzymy projekt Nodejs i instalujemy framework Express

mkdir multiple-thread
cd multiple-thread
npm init -y
npm install express

Przede wszystkim są procesy i wątki. Powinniśmy znać różnice między nimi.

Co to jest proces?

Proces uruchamia program w systemie operacyjnym. Może wykonywać jedno zadanie na raz.

Przyjrzyjmy się bliżej procesowi bliżej. Utwórz plik JS i zarejestruj proces.

//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

Wątki to stosunkowo lekki sposób implementacji wielu ścieżek wykonywania w aplikacji. Na poziomie systemu programy działają obok siebie, a system przydziela czas wykonania każdemu programowi na podstawie jego potrzeb i potrzeb innych programów.

Zakodujmy Express Server z trasami nieblokującymi i trasami blokującymi

Zakodujmy normalną trasę blokowania dla użytkowników. w 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'));

Nie odpowiada, dopóki nie blocking-routeskończy i nie odesła odpowiedzi.

Dlaczego nie odpowiedział nam od razu?

W tym przykładzie pętla for związana z procesorem blokuje główny wątek. Gdy główny wątek jest zablokowany, Node.js nie może przetwarzać żadnych zapytań, dopóki proces związany z procesorem nie zostanie zakończony. więc żadna z innych tras nie /hellodziała podczas /blocking-routeładowania trasy.

Tak więc, jeśli mamy interfejs API, który ma tysiące jednoczesnych GETżądań do nieblokującej trasy, takiej jak /hello, tylko jedno żądanie do /blocking-routezablokuje wszystkie trasy w interfejsie API. Oznacza to, że wszystkie trasy aplikacji przestają odpowiadać.

DAAA IT's javascript, nie możemy użyć innego wątku

Koleś To nie tylko JS. To Nodejs. Możemy odciążyć zadanie związane z procesorem za pomocą wątków roboczych.

Przede wszystkim powinniśmy sprawdzić nasze rdzenie z nprocdowództwem

nproc

output
8

Stwórzmy kolejny plik JS, taki jak worker.js, nazwa zależy od Ciebie. W tym pliku wykonujemy zadania intensywnie korzystające z procesora

//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"));

Uruchommy serwer z node app.js. Otwórz przeglądarkę i przejdź, http://localhost:7333/non-blocking-routezanim zwróci odpowiedź, przejdź do http://localhost:7333/hello. Czy zdałeś sobie z tego sprawę? Odpowiedź otrzymujemy natychmiast. Nie ma blokady. Nie czekaliśmy, aż inne trasy się załadują. Ponieważ zadanie związane z procesorem jest odciążane i działa w innym wątku, a główny wątek obsługuje wszystkie żądania. Możemy zatrudnić więcej pracowników, aby poprawić naszą wydajność. Na przykład mam 8 rdzeni i mogę podzielić swój Proces na 8.

Wniosek

Procesy robocze (wątki) mogą pomóc w operacjach JavaScript intensywnie korzystających z procesora. W przeciwieństwie do child_processlub cluster, worker_threadsmoże współdzielić pamięć.

W tym artykule starałem się to ułatwić i wyjaśnić Jak działa worker_threads?

Dziękuje za przeczytanie

>REF