nodejs는 단일 스레드이지만 여전히 다중 스레드 작업을 허용합니다. 이 기사는 노드 스레드로 시작하여 Nodejs의 다중 스레드 작업에 대해 설명하고 Worker_threads 템플릿을 소개합니다.
이 기사의 테스트 환경:
시스템: macOS Mojave 10.14.2
CPU: 4 코어 2.3 GHz
노드: 10.15.1
[권장 학습: "nodejs tutorial"]
대부분의 사람들은 Node가 싱글 스레드인 것으로 알고 있으므로 Node가 시작된 후 스레드 수는 1이 되어야 합니다. 실험을 통해 확인해 보겠습니다. [추천 학습: "nodejs tutorial"]
setInterval(() => { console.log(new Date().getTime()) }, 3000)
Node 프로세스가 7개의 스레드를 차지하는 것을 볼 수 있습니다. 스레드가 왜 7개인가요?
우리 모두는 Node의 핵심이 v8 엔진이라는 것을 알고 있습니다. Node가 시작되면 v8 인스턴스가 멀티 스레드로 생성됩니다.
그래서 사람들이 Node를 싱글 스레드라고 자주 말한다면 JavaScript의 실행은 싱글 스레드이지만 Javascript의 호스트 환경은 Node든 브라우저든 멀티 스레드라는 뜻입니다.
Node에는 두 가지 컴파일러가 있습니다.
full-codegen: js를 간단하지만 느린 기계적 코드로 간단하고 빠르게 컴파일합니다.
크랭크샤프트: 고성능 실행 코드를 컴파일하는 비교적 복잡한 실시간 최적화 컴파일러입니다.
여전히 위의 예에서는 타이머가 실행되는 동안 파일을 읽습니다.
const fs = require('fs') setInterval(() => { console.log(new Date().getTime()) }, 3000) fs.readFile('./index.html', () => {})
스레드 수가 11개가 됩니다. 이는 일부 IO가 있기 때문입니다. Node에서 작업(DNS, FS) 및 일부 CPU 집약적인 계산(Zlib, Crypto)을 수행하면 Node의 스레드 풀이 활성화되고 스레드 수가 11개가 되므로 스레드 풀의 기본 크기는 4입니다.
스레드 풀 기본 크기를 수동으로 변경할 수 있습니다.
process.env.UV_THREADPOOL_SIZE = 64
코드 한 줄로 스레드를 71로 쉽게 변경할 수 있습니다.
Node의 단일 스레드는 CPU 활용도 부족, 포착되지 않은 예외로 인해 전체 프로그램이 종료되는 등 몇 가지 문제도 발생합니다. 클러스터 모듈은 Node에서 제공되기 때문에 클러스터는 child_process의 캡슐화를 구현하고, fork 방식을 통해 자식 프로세스를 생성하여 다중 프로세스 모델을 구현합니다. 예를 들어 우리가 가장 자주 사용하는 pm2가 그 중 가장 대표적인 예이다.
클러스터 데모를 살펴보겠습니다.
const cluster = require('cluster'); const http = require('http'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { console.log(`主进程 ${process.pid} 正在运行`); for (let i = 0; i < numCPUs; i++) { cluster.fork(); } cluster.on('exit', (worker, code, signal) => { console.log(`工作进程 ${worker.process.pid} 已退出`); }); } else { // 工作进程可以共享任何 TCP 连接。 // 在本例子中,共享的是 HTTP 服务器。 http.createServer((req, res) => { res.writeHead(200); res.end('Hello World'); }).listen(8000); console.log(`工作进程 ${process.pid} 已启动`); }
이때 활동 모니터를 살펴보세요.
총 9개의 프로세스가 있으며 그 중 하나가 메인 프로세스입니다. CPU 수 x CPU 수 코어 = 2 x 4 = 8개의 하위 프로세스 프로세스.
그러므로 child_process도 클러스터도 멀티 스레드 모델이 아니라 멀티 프로세스 모델입니다. 개발자들은 싱글 쓰레드 모델의 문제점을 인지하고 있지만 근본적으로 문제를 해결하지 못하고 멀티 쓰레드를 시뮬레이션하기 위한 멀티 프로세스 방식을 제공하고 있습니다. 이전 실험에서 Node(V8) 자체에는 멀티스레딩 기능이 있지만 개발자는 이 기능을 제대로 활용하지 못하고 대신 Node의 하위 계층에서 제공하는 방식으로 멀티스레딩을 사용한다는 것을 알 수 있습니다. Node 관계자는 다음과 같이 말했습니다:
C++ 애드온을 개발하여 내장된 Node Worker Pool을 사용할 수 있습니다. 이전 버전의 Node에서는 NAN을 사용하여 C++ 애드온을 빌드하고, 최신 버전에서는 node-webworker-threads를 사용하세요. Node의 작업자 풀에 액세스할 수 있는 JavaScript 전용 방법을 제공합니다.
그러나 JavaScript 개발자에게는 Node의 멀티스레딩 기능을 사용하는 표준적이고 사용하기 쉬운 방법이 없었습니다.
Node 10.5.0이 출시될 때까지 공식은 Node에 실제 멀티스레딩 기능을 제공하기 위해 실험적인 모듈 worker_threads을 제공했습니다.
먼저 간단한 데모를 살펴보겠습니다.
const { isMainThread, parentPort, workerData, threadId, MessageChannel, MessagePort, Worker } = require('worker_threads'); function mainThread() { for (let i = 0; i < 5; i++) { const worker = new Worker(__filename, { workerData: i }); worker.on('exit', code => { console.log(`main: worker stopped with exit code ${code}`); }); worker.on('message', msg => { console.log(`main: receive ${msg}`); worker.postMessage(msg + 1); }); } } function workerThread() { console.log(`worker: workerDate ${workerData}`); parentPort.on('message', msg => { console.log(`worker: receive ${msg}`); }), parentPort.postMessage(workerData); } if (isMainThread) { mainThread(); } else { workerThread(); }
위 코드는 메인 스레드에서 5개의 하위 스레드를 열고, 메인 스레드는 하위 스레드에 간단한 메시지를 보냅니다.
由于 worker_thread 目前仍然处于实验阶段,所以启动时需要增加 --experimental-worker
flag,运行后观察活动监视器:
不多不少,正好多了五个子线程。
worker_thread 核心代码
worker_thread 模块中有 4 个对象和 2 个类。
threadId === 0
进行判断的。来看一个进程通信的例子:
const assert = require('assert'); const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('worker_threads'); if (isMainThread) { const worker = new Worker(__filename); const subChannel = new MessageChannel(); worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]); subChannel.port2.on('message', (value) => { console.log('received:', value); }); } else { parentPort.once('message', (value) => { assert(value.hereIsYourPort instanceof MessagePort); value.hereIsYourPort.postMessage('the worker is sending this'); value.hereIsYourPort.close(); }); }
更多详细用法可以查看官方文档。
根据大学课本上的说法:“进程是资源分配的最小单位,线程是CPU调度的最小单位”,这句话应付考试就够了,但是在实际工作中,我们还是要根据需求合理选择。
下面对比一下多线程与多进程:
属性 | 多进程 | 多线程 | 比较 |
---|---|---|---|
数据 | 数据共享复杂,需要用IPC;数据是分开的,同步简单 | 因为共享进程数据,数据共享简单,同步复杂 | 各有千秋 |
CPU、内存 | 占用内存多,切换复杂,CPU利用率低 | 占用内存少,切换简单,CPU利用率高 | 多线程更好 |
销毁、切换 | 创建销毁、切换复杂,速度慢 | 创建销毁、切换简单,速度很快 | 多线程更好 |
coding | 编码简单、调试方便 | 编码、调试复杂 | 多进程更好 |
可靠性 | 进程独立运行,不会相互影响 | 线程同呼吸共命运 | 多进程更好 |
分布式 | 可用于多机多核分布式,易于扩展 | 只能用于多核分布式 | 多进程更好 |
上述比较仅表示一般情况,并不绝对。
work_thread 让 Node 有了真正的多线程能力,算是不小的进步。
更多编程相关知识,请访问:编程视频!!
위 내용은 Nodejs의 다중 스레드 작업에 대한 간략한 토론의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!