NodeJ는 단일 스레드입니까?

Mar 20 2023
Nodejs에서 다른 스레드와 작업하는 방법은 무엇입니까?
Node.js는 단일 스레드에서 JavaScript 코드를 실행합니다. 즉, 코드는 한 번에 하나의 작업만 수행할 수 있습니다.
Nodejs I/O 프로세스

Node.js는 단일 스레드에서 JavaScript 코드를 실행합니다. 즉, 코드는 한 번에 하나의 작업만 수행할 수 있습니다. 그러나 Node.js 자체는 다중 스레드이며 디스크 또는 네트워크 요청에서 파일 읽기와 같은 I/O 작업을 처리하는 `libuv` 라이브러리를 통해 숨겨진 스레드를 제공합니다. 숨겨진 스레드를 사용하여 Node.js는 메인 스레드를 차단하지 않고 코드에서 I/O 요청을 할 수 있는 비동기 메서드를 제공합니다.

Node.js는 우리에게 worker-threads 모듈을 소개했습니다. 이 모듈을 사용하면 스레드를 생성하고 Javascript 작업을 병렬로 실행할 수 있습니다. 스레드가 작업을 완료하면 메인 스레드에서 사용할 수 있도록 결과가 포함된 메시지를 메인 스레드로 보냅니다. 작업자 스레드는 기본 스레드를 차단하지 않으므로 작업을 여러 부분으로 나누어 최적화하여 작업을 분배할 수 있습니다.

예를 들어 자세히 살펴보겠습니다.

Nodejs 프로젝트를 생성하고 Express 프레임워크를 설치하고 있습니다.

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

우선 프로세스와 스레드가 있습니다. 우리는 그들 사이의 차이점을 알아야 합니다.

프로세스란 무엇입니까?

프로세스가 OS에서 프로그램을 실행하고 있습니다. 한 번에 하나의 작업을 실행할 수 있습니다.

프로세스를 자세히 살펴 보겠습니다. 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

스레드는 애플리케이션 내에서 여러 실행 경로를 구현하는 비교적 가벼운 방법입니다. 시스템 수준에서 프로그램은 나란히 실행되며 시스템은 필요와 다른 프로그램의 필요에 따라 각 프로그램에 실행 시간을 할당합니다.

non-blocking route와 blocking route로 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응답을 다시 보냈습니다.

왜 즉시 응답하지 않았습니까?

이 예에서 CPU 바운드 for 루프는 기본 스레드를 차단합니다. 메인 스레드가 차단된 동안 Node.js는 CPU 바인딩 프로세스가 종료될 때까지 쿼리를 처리할 수 없습니다. /hello따라서 경로가 로드될 때 작동하는 것과 같은 다른 경로는 없습니다 /blocking-route.

GET따라서 비차단 경로에 대해 수천 건의 동시 요청이 있는 API가 있는 경우 에 /hello대한 요청 하나만 /blocking-routeAPI의 모든 경로를 차단합니다. 즉, 모든 애플리케이션 경로가 응답하지 않게 만듭니다.

DAAA IT의 javascript, 다른 스레드를 사용할 수 없습니다.

Dudeeee JS 뿐만이 아닙니다. 노드제이입니다. 작업자 스레드를 사용하여 CPU 바인딩 작업을 오프로드할 수 있습니다.

우선, nproc명령 으로 코어를 확인해야 합니다.

nproc

output
8

와 같은 또 다른 JS 파일을 만들어 봅시다 worker.js. 이름은 당신에게 달려 있습니다. 이 파일에서는 CPU를 많이 사용하는 작업을 수행합니다.

//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. 당신은 그것을 깨달았습니까? 즉시 응답을 받습니다. 차단이 없습니다. 다른 경로가 로드를 완료할 때까지 기다리지 않았습니다. CPU 바운드 작업이 오프로드되고 다른 스레드에서 작동하고 기본 스레드가 모든 요청을 처리하기 때문입니다. 더 많은 작업자를 사용하여 성능을 향상시킬 수 있습니다. 예를 들어 코어가 8개이고 프로세스를 8개로 나눌 수 있습니다.

결론

작업자(스레드)는 CPU를 많이 사용하는 JavaScript 작업을 도울 수 있습니다. child_process또는 cluster와 달리 worker_threads메모리를 공유할 수 있습니다.

이 기사에서는 쉽게 설명하고 worker_threads 작동 방식을 설명하려고 노력했습니다.

읽어 주셔서 감사합니다

>참조