Home  >  Article  >  Web Front-end  >  A brief discussion on multi-threaded operations in Nodejs

A brief discussion on multi-threaded operations in Nodejs

青灯夜游
青灯夜游forward
2021-06-23 10:31:354445browse

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.

A brief discussion on multi-threaded operations in Nodejs

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"]

Start with Node threads

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)

A brief discussion on multi-threaded operations in Nodejs

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.

  • Main thread: compile and execute code.
  • Compile/optimize thread: When the main thread executes, the code can be optimized.
  • Analyzer thread: records the analysis code running time to provide a basis for Crankshaft to optimize code execution.
  • Several threads for garbage collection.

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.

Some asynchronous IO will occupy additional threads

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', () => {})

A brief discussion on multi-threaded operations in Nodejs

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.

A brief discussion on multi-threaded operations in Nodejs

#cluster Is it multi-threaded?

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(&#39;exit&#39;, (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  // 工作进程可以共享任何 TCP 连接。
  // 在本例子中,共享的是 HTTP 服务器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end(&#39;Hello World&#39;);
  }).listen(8000);
  console.log(`工作进程 ${process.pid} 已启动`);
}

At this time, look at the activity monitor:

A brief discussion on multi-threaded operations in Nodejs

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

True - Node multi-threading

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(&#39;worker_threads&#39;);

function mainThread() {
  for (let i = 0; i < 5; i++) {
    const worker = new Worker(__filename, { workerData: i });
    worker.on(&#39;exit&#39;, code => { console.log(`main: worker stopped with exit code ${code}`); });
    worker.on(&#39;message&#39;, msg => {
      console.log(`main: receive ${msg}`);
      worker.postMessage(msg + 1);
    });
  }
}

function workerThread() {
  console.log(`worker: workerDate ${workerData}`);
  parentPort.on(&#39;message&#39;, 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,运行后观察活动监视器:

A brief discussion on multi-threaded operations in Nodejs

不多不少,正好多了五个子线程。

worker_thread 模块

worker_thread 核心代码

worker_thread 模块中有 4 个对象和 2 个类。

  • isMainThread: 是否是主线程,源码中是通过 threadId === 0 进行判断的。
  • MessagePort: 用于线程之间的通信,继承自 EventEmitter。
  • MessageChannel: 用于创建异步、双向通信的通道实例。
  • threadId: 线程 ID。
  • Worker: 用于在主线程中创建子线程。第一个参数为 filename,表示子线程执行的入口。
  • parentPort: 在 worker 线程里是表示父进程的 MessagePort 类型的对象,在主线程里为 null
  • workerData: 用于在主进程中向子进程传递数据(data 副本)

来看一个进程通信的例子:

const assert = require(&#39;assert&#39;);
const {
  Worker,
  MessageChannel,
  MessagePort,
  isMainThread,
  parentPort
} = require(&#39;worker_threads&#39;);
if (isMainThread) {
  const worker = new Worker(__filename);
  const subChannel = new MessageChannel();
  worker.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1]);
  subChannel.port2.on(&#39;message&#39;, (value) => {
    console.log(&#39;received:&#39;, value);
  });
} else {
  parentPort.once(&#39;message&#39;, (value) => {
    assert(value.hereIsYourPort instanceof MessagePort);
    value.hereIsYourPort.postMessage(&#39;the worker is sending this&#39;);
    value.hereIsYourPort.close();
  });
}

更多详细用法可以查看官方文档

多进程 vs 多线程

根据大学课本上的说法:“进程是资源分配的最小单位,线程是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!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete