Node.js躬行记(23)——Worker threads

2022-09-26 00:00:00 创建 执行 线程 主线 销毁

  Node.js 官方提供了 Cluster 和 Child process 创建子进程,通过 Worker threads 模块创建子线程。但前者无法共享内存,通信必须使用 JSON 格式,有一定的局限性和性能问题。后者更轻量,并且可以共享内存,通过传输 ArrayBuffer 实例或共享 SharedArrayBuffer 实例来做到这一点,即数据格式没有太多要求。但是要注意,数据中不能包含函数。

  Worker threads 从 Node V12 开始成为正式标准,其对于执行 CPU 密集型的操作很有用,而对 I/O 密集型工作没有多大帮助。 Node.js 内置的异步 I/O 操作要比它效率更高。注意,Worker threads 是基于 Node.js 架构的多工作线程,如下图所示。在每个工作线程中,都会包含 V8 和 libuv,即都包含Event Loop。

  


一、线程池

  创建、执行、销毁一个 Worker 的开销是很大的,所以需要实现一个线程池(Worker Pool),在初始化时创建有限数量的 Worker 并加载单一的 worker.js,主线程和 Worker 可进行进程间通信,当所有任务完成后,这些 Worker 将会被统一销毁。

  在 Worker 中通过 parentPort.postMessage() 向主线程发送消息,而在主线程中可以通过 worker.on('message') 接收发送过来的消息,worker 是一个 Worker 实例,例如 new Worker(filePath)。

  下面是一个官方示例,isMainThread 可判断当前是否是主线程,workerData 是传递给 Worker 的数据。

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');
if (isMainThread) {
  module.exports = function parseJSAsync(script) {
    return new Promise((resolve, reject) => {
      const worker = new Worker(__filename, {
        workerData: script
      });
      worker.on('message', resolve);
      worker.on('error', reject);
      worker.on('exit', (code) => {
        if (code !== 0)
          reject(new Error(`Worker stopped with exit code ${code}`));
      });
    });
  };
} else {
  const script = workerData;
  parentPort.postMessage(script);
}

相关文章