Worker
Nodejs는 하나의 프로세스, 하나의 스레드, 하나의 이벤트루프에 의해 실행된다.
즉, 하나의 코드이고, 하나의 실행이 되는 것이다.
Node.js코드는 병렬로 실행되지 않는다.
이 때문에 단점이 있는데, CPU자원을 많이 사용하는 코드가 있으면 이 코드가 다른 프로세스가 실행되는걸 차단할 수도 있다.
이런 경우, Worker가 효과적이다.
Worker의 목적은 I/O 작업이 아닌 CPU집약적인 작업 퍼포먼스를 향상시키는 것이다.
아래 그림과 같은 느낌이라고 한다.
메인스레드와 통신
Worker스레드는 메인스레드(멀티스레드를 사용하기전 코드들이 실행되는 곳)에서 new Worker를 통해 만들어진다.
메인스레드에서 만든 Worker에 on 형태로 이벤트를 달아서 워커스레드와 통신할 수 있다.
const { Worker } = require('worker_threads')
const worker = new Worker('./worker.js')
worker.on('message', (value) => {
console.log('워커', value)
})
worker.on('exit', (value) => {
console.log('워커 끝~');
})
위의 코드는 같은 디렉토리내의 worker.js파일 내부의 코드들을 멀티스레드로 구동시키겠다는 뜻이다.
Worker 작동시키기
Worker를 만들었다고 자기가 알아서 일을 하는게 아니다.
메인스레드에서 일하라고 명령(postMessage)을 해야 Worker가 일을한다.
const { Worker } = require('worker_threads')
const worker = new Worker('./worker.js')
worker.on('message', (value) => {
console.log('워커', value)
})
worker.on('exit', (value) => {
console.log('워커 끝~');
})
worker.postMessage("일해라");
이렇게 만들어진 워커 스레드는 메인 스레드와 Port를 통해 통신한다.
꼭 이벤트 리스너처럼 사용하는데,
import { parentPort } from 'worker_threads';
parentPort.on('message', (message) => {
parentPort.postMessage("일시작!");
parentPort.close();
});
parentPort.on을 통해 메인스레드에서 출발한 메시지를 받는다. 콜백함수를 받아서 메인스레드에서 전송된 데이터를 받을 수 있다.
parentProt.close를 하면 워커가 일을 끝내고, 이후 오는 메시지에는 답하지 않는다.
위와같이 코드를 짜고 메인스레드를 아래와 같이 작성했다고 가정해보면 결과는 이렇다.
import { Worker } from 'worker_threads';
const worker = new Worker('./worker.js');
worker.on('message', (message) => {
console.log(message);
});
worker.postMessage(1); // 일시작!
worker.postMessage(1); // 이후로 메시지 응답 없음
worker.postMessage(1);
worker.postMessage(1);
작업은 개발자가 할당해주기.
여기까지 내용을 잘 이해했다면 알 수 있을텐데, 멀티스레드라고 해서 알아서 자원을 공유하고, 알아서 임무를 나눠갖는게 아니다. 모든걸 개발자가 처리해주어야한다.
2편에서는 연습을 해보겠다.