Although nodejs is single-threaded, it still allows multi-threaded operations. This article will start with Node threads, talk about multi-threaded operations in Nodejs, and introduce the worker_threads template.
The test environment for this article:
System: macOS Mojave 10.14.2
CPU: 4 cores 2.3 GHz
Node: 10.15.1
[Recommended learning: "nodejs Tutorial"]
Most people understand Node It is single-threaded, so the number of threads should be 1 after Node is started. Let's do an experiment and see. [Recommended study: "nodejs Tutorial"]
setInterval(() => { console.log(new Date().getTime()) }, 3000)
You can see that the Node process occupies 7 threads. Why are there 7 threads?
We all know that the core of Node is the v8 engine. After Node is started, an instance of v8 will be created. This instance is multi-threaded.
So everyone often says that Node is single-threaded, which means that the execution of JavaScript is single-threaded, but the host environment of Javascript, whether it is Node or the browser, is multi-threaded.
Node has two compilers:
full-codegen: simply and quickly compile js into simple but slow machine code.
Crankshaft: A relatively complex real-time optimization compiler that compiles high-performance executable code.
Still the above example, we read a file while the timer is executing:
const fs = require('fs') setInterval(() => { console.log(new Date().getTime()) }, 3000) fs.readFile('./index.html', () => {})
The number of threads becomes 11. This is because there are some IO operations (DNS, FS) and some CPU-intensive calculations (Zlib, Crypto) in Node that enable Node The thread pool, and the default size of the thread pool is 4, because the number of threads becomes 11.
We can manually change the default size of the thread pool:
process.env.UV_THREADPOOL_SIZE = 64
Easily change the threads to 71 with one line of code.
The single thread of Node also brings some problems, such as insufficient utilization of the CPU, an uncaught exception may cause the entire program to exit, etc. Because the cluster module is provided in Node, cluster implements the encapsulation of child_process and implements the multi-process model by creating child processes through the fork method. For example, pm2, which we use most often, is the best representative among them.
Let’s look at a cluster demo:
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} 已启动`); }
At this time, look at the activity monitor:
There are 9 processes in total, among which A main process, the number of CPUs x the number of CPU cores = 2 x 4 = 8 child processes.
So neither child_process nor cluster is a multi-thread model, but a multi-process model. Although developers are aware of the problems of the single-threaded model, they do not fundamentally solve the problem and provide a multi-process method to simulate multi-threading. From the previous experiments, we can see that although Node (V8) itself has multi-threading capabilities, developers cannot make good use of this capability. Instead, they use multi-threading in some ways provided by the bottom layer of Node. Node official said:
You can use the built-in Node Worker Pool by developing a C addon. On older versions of Node, build your C addon using NAN, and on newer versions use N-API . node-webworker-threads offers a JavaScript-only way to access Node's Worker Pool.
But for JavaScript developers, there has never been a standard and easy-to-use way to use Node's multi-threading capabilities. .
Until the release of Node 10.5.0, the official gave an experimental module worker_threads to Node Provides true multi-threading capabilities.
Let’s take a look at the simple demo first:
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(); }
The above code opens five sub-threads in the main thread, and the main thread sends simple messages to the sub-threads.
由于 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 有了真正的多线程能力,算是不小的进步。
更多编程相关知识,请访问:编程视频!!
The above is the detailed content of A brief discussion on multi-threaded operations in Nodejs. For more information, please follow other related articles on the PHP Chinese website!