Node.js v20.2.0 文档


目录

工作线程#

稳定性: 2 - 稳定

源代码: lib/worker_threads.js

node:worker_threads 模块允许使用并行执行 JavaScript 的线程。 要访问它:

const worker = require('node:worker_threads'); 

工作线程对于执行 CPU 密集型的 JavaScript 操作很有用。 它们对 I/O 密集型的工作帮助不大。 Node.js 内置的异步 I/O 操作比工作线程更高效。

child_processcluster 不同,worker_threads 可以共享内存。 它们通过传输 ArrayBuffer 实例或共享 SharedArrayBuffer 实例来实现。

const {
  Worker, isMainThread, parentPort, workerData,
} = require('node: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 { parse } = require('some-js-parsing-library');
  const script = workerData;
  parentPort.postMessage(parse(script));
} 

上面的示例为每个 parseJSAsync() 调用衍生工作线程。 在实践中,为这些类型的任务使用工作线程池。 否则,创建工作线程的开销可能会超过其收益。

当实现工作线程池时,使用 AsyncResource API 通知诊断工具(例如提供异步的堆栈跟踪)有关任务与其结果之间的相关性。 有关示例实现,请参阅 async_hooks 文档中的 "Using AsyncResource for a Worker thread pool"

默认情况下,工作线程继承非进程特定的选项。 参考 Worker constructor options 了解如何自定义工作线程选项,特别是 argvexecArgv 选项。

worker.getEnvironmentData(key)#

  • key <any> 任何可以用作 <Map> 键的任意、可克隆的 JavaScript 值。
  • 返回: <any>

在工作线程中,worker.getEnvironmentData() 返回传给衍生线程的 worker.setEnvironmentData() 的数据的克隆。 每个新的 Worker 都会自动接收到自己的环境数据的副本。

const {
  Worker,
  isMainThread,
  setEnvironmentData,
  getEnvironmentData,
} = require('node:worker_threads');

if (isMainThread) {
  setEnvironmentData('Hello', 'World!');
  const worker = new Worker(__filename);
} else {
  console.log(getEnvironmentData('Hello'));  // Prints 'World!'.
} 

worker.isMainThread#

如果此代码不在 Worker 线程内运行,则为 true

const { Worker, isMainThread } = require('node:worker_threads');

if (isMainThread) {
  // This re-loads the current file inside a Worker instance.
  new Worker(__filename);
} else {
  console.log('Inside Worker!');
  console.log(isMainThread);  // Prints 'false'.
} 

worker.markAsUntransferable(object)#

将对象标记为不可传输。 如果 object 出现在 port.postMessage() 调用的传输列表中,则忽略它。

特别是,这对于可以克隆而不是传输的对象,以及被发送方的其他对象使用的对象来说是有意义的。 例如,Node.js 将 ArrayBuffer 用于 Buffer pool 标记。

此操作无法撤消。

const { MessageChannel, markAsUntransferable } = require('node:worker_threads');

const pooledBuffer = new ArrayBuffer(8);
const typedArray1 = new Uint8Array(pooledBuffer);
const typedArray2 = new Float64Array(pooledBuffer);

markAsUntransferable(pooledBuffer);

const { port1 } = new MessageChannel();
port1.postMessage(typedArray1, [ typedArray1.buffer ]);

// The following line prints the contents of typedArray1 -- it still owns
// its memory and has been cloned, not transferred. Without
// `markAsUntransferable()`, this would print an empty Uint8Array.
// typedArray2 is intact as well.
console.log(typedArray1);
console.log(typedArray2); 

浏览器中没有与此 API 等效的 API。

worker.moveMessagePortToContext(port, contextifiedSandbox)#

MessagePort 传输到不同的 vm 上下文 原始的 port 对象变得不可用,返回的 MessagePort 实例取而代之。

返回的 MessagePort 是目标上下文中的对象,并且继承自其全局的 Object 类。 传给 port.onmessage() 监听器的对象也在目标上下文中创建并且从其全局的 Object 类继承。

但是,创建的 MessagePort 不再继承 EventTarget,只有 port.onmessage() 可以使用它来接收事件。

worker.parentPort#

如果此线程是 Worker,则这是允许与父线程通信的 MessagePort。 使用 parentPort.postMessage() 发送的消息在使用 worker.on('message') 的父线程中可用,使用 worker.postMessage() 从父线程发送的消息在使用 parentPort.on('message') 的该线程中可用。

const { Worker, isMainThread, parentPort } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename);
  worker.once('message', (message) => {
    console.log(message);  // Prints 'Hello, world!'.
  });
  worker.postMessage('Hello, world!');
} else {
  // When a message from the parent thread is received, send it back:
  parentPort.once('message', (message) => {
    parentPort.postMessage(message);
  });
} 

worker.receiveMessageOnPort(port)#

从给定的 MessagePort 接收消息。 如果没有消息可用,则返回 undefined,否则返回具有单个 message 属性的对象,该对象包含消息负载,对应于 MessagePort 队列中最旧的消息。

const { MessageChannel, receiveMessageOnPort } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();
port1.postMessage({ hello: 'world' });

console.log(receiveMessageOnPort(port2));
// Prints: { message: { hello: 'world' } }
console.log(receiveMessageOnPort(port2));
// Prints: undefined 

当使用此函数时,不会触发 'message' 事件,也不会调用 onmessage 监听器。

worker.resourceLimits#

在这个工作线程中提供了一组 JS 引擎资源约束。 如果将 resourceLimits 选项传给 Worker 构造函数,则这与其值匹配。

如果在主线程中使用此,则其值为空对象。

worker.SHARE_ENV#

可以作为 Worker 构造函数的 env 选项传入的特殊值,表示当前线程和工作线程应该共享对同一组环境变量的读写访问。

const { Worker, SHARE_ENV } = require('node:worker_threads');
new Worker('process.env.SET_IN_WORKER = "foo"', { eval: true, env: SHARE_ENV })
  .on('exit', () => {
    console.log(process.env.SET_IN_WORKER);  // Prints 'foo'.
  }); 

worker.setEnvironmentData(key[, value])#

  • key <any> 任何可以用作 <Map> 键的任意、可克隆的 JavaScript 值。
  • value <any> 任何任意的、可克隆的 JavaScript 值都将被克隆并自动传给所有新的 Worker 实例。 如果 value 作为 undefined 传入,则 key 之前设置的任何值都将被删除。

worker.setEnvironmentData() API 设置当前线程中 worker.getEnvironmentData() 的内容以及从当前上下文产生的所有新 Worker 实例。

worker.threadId#

当前线程的整数标识符。 在对应的工作线程对象上(如果有的话),可以作为 worker.threadId 使用。 此值对于单个进程中的每个 Worker 实例都是唯一的。

worker.workerData#

任意的 JavaScript 值,其中包含传给该线程的 Worker 构造函数的数据的克隆。

根据 HTML 结构化克隆算法,数据被克隆,就像使用 postMessage() 一样。

const { Worker, isMainThread, workerData } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: 'Hello, world!' });
} else {
  console.log(workerData);  // Prints 'Hello, world!'.
} 

类:BroadcastChannel extends EventTarget#

BroadcastChannel 的实例允许与绑定到相同通道名称的所有其他 BroadcastChannel 实例进行异步的一对多通信。

'use strict';

const {
  isMainThread,
  BroadcastChannel,
  Worker,
} = require('node:worker_threads');

const bc = new BroadcastChannel('hello');

if (isMainThread) {
  let c = 0;
  bc.onmessage = (event) => {
    console.log(event.data);
    if (++c === 10) bc.close();
  };
  for (let n = 0; n < 10; n++)
    new Worker(__filename);
} else {
  bc.postMessage('hello from every worker');
  bc.close();
} 

new BroadcastChannel(name)#

  • name <any> 要连接的通道名称。 任何可以使用 `${name}` 转换为字符串的 JavaScript 值都是允许的。

broadcastChannel.close()#

关闭 BroadcastChannel 连接。

broadcastChannel.onmessage#

  • 类型: <Function> 当接收到消息时,使用单个 MessageEvent 参数调用。

broadcastChannel.onmessageerror#

  • 类型: <Function> 使用接收到的消息调用不能反序列化。

broadcastChannel.postMessage(message)#

  • message <any> 任何可克隆的 JavaScript 值。

broadcastChannel.ref()#

unref() 的相反。 如果它是唯一剩下的活动句柄(默认行为),则在先前 unref()ed BroadcastChannel 上调用 ref() 不会让程序退出。 如果端口是 ref() 的,则再次调用 ref() 没有效果。

broadcastChannel.unref()#

如果这是事件系统中唯一的活动句柄,则在广播通道上调用 unref() 允许线程退出。 如果广播通道已经 unref(),则再次调用 unref() 无效。

类:MessageChannel#

worker.MessageChannel 类的实例代表异步的双向通信通道。 MessageChannel 没有自己的方法。 new MessageChannel() 产生具有 port1port2 属性的对象,其引用链接的 MessagePort 实例。

const { MessageChannel } = require('node:worker_threads');

const { port1, port2 } = new MessageChannel();
port1.on('message', (message) => console.log('received', message));
port2.postMessage({ foo: 'bar' });
// Prints: received { foo: 'bar' } from the `port1.on('message')` listener 

类:MessagePort#

worker.MessagePort 类的实例代表异步双向通信通道的一端。 它可以用来在不同的Worker之间传输结构化数据、内存区域和其他MessagePort

此实现匹配 浏览器MessagePort

事件:'close'#

一旦通道的任一侧断开连接,则会触发 'close' 事件。

const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();

// Prints:
//   foobar
//   closed!
port2.on('message', (message) => console.log(message));
port2.on('close', () => console.log('closed!'));

port1.postMessage('foobar');
port1.close(); 

事件:'message'#

为任何传入消息触发 'message' 事件,其中包含 port.postMessage() 的克隆输入。

此事件的监听器接收传给 postMessage()value 参数的副本,没有其他参数。

事件:'messageerror'#

当反序列化消息失败时,则会触发 'messageerror' 事件。

目前,当在接收端实例化已发布的 JS 对象时发生错误时,则会触发此事件。 这种情况很少见,但可能会发生,例如,当某些 Node.js API 对象在 vm.Context 中接收到时(Node.js API 当前不可用)。

port.close()#

禁止在连接的任一端进一步发送消息。 当此 MessagePort 上不会发生进一步的通信时,可以调用此方法。

'close' 事件 在作为通道一部分的两个 MessagePort 实例上触发。

port.postMessage(value[, transferList])#

向该通道的接收端发送 JavaScript 值。 value 以与 HTML 结构化克隆算法 兼容的方式传输。

特别是与 JSON 的显着区别是:

const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();

port1.on('message', (message) => console.log(message));

const circularData = {};
circularData.foo = circularData;
// Prints: { foo: [Circular] }
port2.postMessage(circularData); 

transferList 可能是 ArrayBufferMessagePortFileHandle 对象的列表。 传输后,它们在通道的发送端不再可用(即使它们不包含在 value 中)。 与 子进程 不同,目前不支持传输句柄,例如网络套接字。

如果 value 包含 SharedArrayBuffer 实例,则可以从任一线程访问它们。 它们不能在 transferList 中列出。

value 可能仍然包含不在 transferList 中的 ArrayBuffer 个实例; 在这种情况下,底层内存被复制而不是移动。

const { MessageChannel } = require('node:worker_threads');
const { port1, port2 } = new MessageChannel();

port1.on('message', (message) => console.log(message));

const uint8Array = new Uint8Array([ 1, 2, 3, 4 ]);
// This posts a copy of `uint8Array`:
port2.postMessage(uint8Array);
// This does not copy data, but renders `uint8Array` unusable:
port2.postMessage(uint8Array, [ uint8Array.buffer ]);

// The memory for the `sharedUint8Array` is accessible from both the
// original and the copy received by `.on('message')`:
const sharedUint8Array = new Uint8Array(new SharedArrayBuffer(4));
port2.postMessage(sharedUint8Array);

// This transfers a freshly created message port to the receiver.
// This can be used, for example, to create communication channels between
// multiple `Worker` threads that are children of the same parent thread.
const otherChannel = new MessageChannel();
port2.postMessage({ port: otherChannel.port1 }, [ otherChannel.port1 ]); 

消息对象立即克隆,发布后可修改,无副作用。

有关此 API 背后的序列化和反序列化机制的更多信息,请参阅 node:v8 模块的序列化 API

传输 TypedArray 和缓冲区时的注意事项#

所有 TypedArrayBuffer 实例都是对底层 ArrayBuffer 的视图。 也就是说,实际存储原始数据的是 ArrayBuffer,而 TypedArrayBuffer 对象提供了查看和操作数据的方式。 在同一个 ArrayBuffer 实例上创建多个视图是可能且常见的。 使用传输列表传输 ArrayBuffer 时必须非常小心,因为这样做会导致共享同一个 ArrayBuffer 的所有 TypedArrayBuffer 实例变得不可用。

const ab = new ArrayBuffer(10);

const u1 = new Uint8Array(ab);
const u2 = new Uint16Array(ab);

console.log(u2.length);  // prints 5

port.postMessage(u1, [u1.buffer]);

console.log(u2.length);  // prints 0 

对于 Buffer 实例,具体来说,底层 ArrayBuffer 是否可以被传输或克隆完全取决于实例是如何创建的,这通常无法可靠地确定。

ArrayBuffer 可以用 markAsUntransferable() 标记来表示它应该总是被克隆并且永远不会被传输。

根据 Buffer 实例的创建方式,它可能拥有也可能不拥有其底层 ArrayBuffer。 除非知道 Buffer 实例拥有它,否则不得传输 ArrayBuffer。 特别是,对于从内部 Buffer 池(例如使用 Buffer.from()Buffer.allocUnsafe())创建的 Buffer,传输它们是不可能的,它们总是被克隆,这会发送整个 Buffer 池的副本。 此行为可能会带来意想不到的更高内存使用率和可能的安全问题。

有关 Buffer 池化的更多详细信息,请参阅 Buffer.allocUnsafe()

使用 Buffer.alloc()Buffer.allocUnsafeSlow() 创建的 Buffer 实例的 ArrayBuffer 始终可以传输,但这样做会使这些 ArrayBuffer 的所有其他现有视图不可用。

使用原型、类、以及访问器克隆对象时的注意事项#

因为对象克隆使用 HTML 结构化克隆算法,所以不保留不可枚举的属性、属性访问器和对象原型。 特别是,Buffer 对象在接收方将被读取为纯 Uint8Array,而 JavaScript 类的实例将被克隆为纯 JavaScript 对象。

const b = Symbol('b');

class Foo {
  #a = 1;
  constructor() {
    this[b] = 2;
    this.c = 3;
  }

  get d() { return 4; }
}

const { port1, port2 } = new MessageChannel();

port1.onmessage = ({ data }) => console.log(data);

port2.postMessage(new Foo());

// Prints: { c: 3 } 

此限制扩展到许多内置对象,例如全局的 URL 对象:

const { port1, port2 } = new MessageChannel();

port1.onmessage = ({ data }) => console.log(data);

port2.postMessage(new URL('https://example.org'));

// Prints: { } 

port.hasRef()#

稳定性: 1 - 实验

如果为 true,则 MessagePort 对象将使 Node.js 事件循环保持活动状态。

port.ref()#

unref() 的相反。 如果它是唯一剩下的活动句柄(默认行为),则在以前的 unref()ed 端口上调用 ref() 不会让程序退出。 如果端口是 ref() 的,则再次调用 ref() 没有效果。

如果使用 .on('message') 绑定或删除监听器,则根据事件的监听器是否存在,端口将自动进行 ref()unref()

port.start()#

开始在此 MessagePort 上接收消息。 当将此端口用作事件触发器时,一旦绑定了 'message' 监听器,则会自动调用它

此方法与 Web MessagePort API 相同。 在 Node.js 中,只有在没有事件监听器时才用于忽略消息。 Node.js 在处理 .onmessage 方面也有分歧。 设置它会自动调用 .start(),但取消设置它会让消息排队,直到设置新的处理程序或端口被丢弃。

port.unref()#

如果这是事件系统中唯一的活动句柄,则在端口上调用 unref() 允许线程退出。 如果端口已经 unref(),则再次调用 unref() 无效。

如果使用 .on('message') 绑定或删除监听器,则根据事件的监听器是否存在,端口将自动进行 ref()unref()

类:Worker#

Worker 类代表独立的 JavaScript 执行线程。 大多数 Node.js API 都可以在其中使用。

工作线程环境中的显着差异是:

在其他 Worker 中创建 Worker 实例是可能的。

网络工作线程node:cluster 模块一样,可以通过线程间消息传递实现双向通信。 在内部,Worker 有一对内置的 MessagePort,它们在创建 Worker 时已经相互关联。 虽然父端的 MessagePort 对象没有直接暴露,但其功能通过父线程 Worker 对象上的 worker.postMessage()worker.on('message') 事件暴露。

要创建自定义消息通道(鼓励使用默认全局通道,因为它有助于分离关注点),用户可以在任一线程上创建 MessageChannel 对象,并通过预先存在的线程将 MessageChannel 上的 MessagePort 之一传递给另一个线程 通道,例如全局通道。

有关如何传递消息以及可以成功通过线程屏障传输的 JavaScript 值类型的更多信息,请参阅 port.postMessage()

const assert = require('node:assert');
const {
  Worker, MessageChannel, MessagePort, isMainThread, parentPort,
} = require('node: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();
  });
} 

new Worker(filename[, options])#

  • filename <string> | <URL> 工作线程的主脚本或模块的路径。 必须是以 ./../ 开头的绝对路径或相对路径(即相对于当前工作目录)、或者是使用 file:data: 协议的 WHATWG URL 对象 使用 data: 网址 时,将使用 ECMAScript 模块加载器 根据 MIME 类型解释数据。 如果 options.evaltrue,则这是包含 JavaScript 代码(而不是路径)的字符串。
  • options <Object>
    • argv <any[]> 将被字符串化并附加到工作线程中的 process.argv 的参数列表。 这与 workerData 非常相似,但这些值在全局的 process.argv 上可用,就好像它们作为 CLI 选项传给脚本一样。
    • env <Object> 如果设置,则指定工作线程内 process.env 的初始值。 worker.SHARE_ENV作为一个特殊的值,可以用来指定父线程和子线程共享环境变量; 在这种情况下,对一个线程的 process.env 对象的更改也会影响另一个线程。 默认值: process.env
    • eval <boolean> 如果 true 并且第一个参数是 string,则将构造函数的第一个参数解释为一旦工作线程在线就执行的脚本。
    • execArgv <string[]> 传给工作线程的 node CLI 选项的列表。 不支持 V8 选项(如 --max-old-space-size)和影响进程的选项(如 --title)。 如果设置,则此在工作线程内部作为 process.execArgv 提供。 默认情况下,选项继承自父线程。
    • stdin <boolean> 如果设置为 true,则 worker.stdin 提供其内容在工作线程中显示为 process.stdin 的可写流。 默认情况下,不提供任何数据。
    • stdout <boolean> 如果设置为 true,则 worker.stdout 不会自动通过管道传输到父线程中的 process.stdout
    • stderr <boolean> 如果设置为 true,则 worker.stderr 不会自动通过管道传输到父线程中的 process.stderr
    • workerData <any> 任何被克隆并作为 require('node:worker_threads').workerData 可用的 JavaScript 值。 克隆如 HTML 结构化克隆算法 中所述发生,如果无法克隆对象(例如,因为它包含 function),则会抛出错误。
    • trackUnmanagedFds <boolean> 如果设置为 true,则工作线程会跟踪通过 fs.open()fs.close() 管理的原始文件描述符,并在工作线程退出时关闭它们,类似于网络套接字或通过 FileHandle API 管理的文件描述符等其他资源。 所有嵌套的 Worker 都会自动继承此选项。 默认值: true
    • transferList <Object[]> 如果在 workerData 中传入一个或多个类似 MessagePort 的对象,则这些条目需要 transferList 或抛出 ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST。 有关详细信息,请参阅 port.postMessage()
    • resourceLimits <Object> 新的 JS 引擎实例的一组可选资源限制。 达到这些限制会导致 Worker 实例终止。 这些限制只影响 JS 引擎,不影响外部数据,包括没有 ArrayBuffer。 即使设置了这些限制,如果遇到全局内存不足的情况,进程仍可能会中止。
      • maxOldGenerationSizeMb <number> 主堆的最大大小 (以 MB 为单位)。 如果设置了命令行参数 --max-old-space-size,它将覆盖此设置。
      • maxYoungGenerationSizeMb <number> 最近创建的对象的最大堆空间大小。 如果设置了命令行参数 --max-semi-space-size,它将覆盖此设置。
      • codeRangeSizeMb <number> 用于生成代码的预分配内存范围的大小。
      • stackSizeMb <number> 线程的默认最大堆栈大小。 较小的值可能会导致工作线程实例无法使用。 默认值: 4
    • name <string> 一个可选的 name 附加到工作人员标头用于调试/识别目的,使最终标头为 [worker ${id}] ${name}默认值: ''

事件:'error'#

如果工作线程抛出未捕获的异常,则会触发 'error' 事件。 在这种情况下,工作线程被终止。

事件:'exit'#

一旦工作线程停止,则会触发 'exit' 事件。 如果工作线程是通过调用 process.exit() 退出的,则 exitCode 参数就是传入的退出码。 如果工作线程被终止,则 exitCode 参数为 1

这是任何 Worker 实例触发的最终事件。

事件:'message'#

当工作线程调用 require('node:worker_threads').parentPort.postMessage() 时,则会触发 'message' 事件。 详情请见 port.on('message') 事件。

Worker 对象上触发 'exit' 事件 之前,从工作线程发送的所有消息都会触发。

事件:'messageerror'#

当反序列化消息失败时,则会触发 'messageerror' 事件。

事件:'online'#

当工作线程开始执行 JavaScript 代码时,则会触发 'online' 事件。

worker.getHeapSnapshot([options])#

  • options <Object>
    • exposeInternals <boolean> 如果为真,则在堆快照中公开内部结构。 默认值: false
    • exposeNumericValues <boolean> 如果为真,则在人工字段中公开数值。 默认值: false
  • 返回: <Promise> 对包含 V8 堆快照的可读流的 promise

返回工作线程当前状态的 V8 快照的可读流。 有关详细信息,请参阅 v8.getHeapSnapshot()

如果 Worker 线程不再运行,这可能会发生之前的 'exit' 事件 is emitted, the returned Promise is rejected immediately with an ERR_WORKER_NOT_RUNNING 错误。

worker.performance#

可用于从工作线程实例查询性能信息的对象。 类似于perf_hooks.performance

performance.eventLoopUtilization([utilization1[, utilization2]])#

perf_hooks eventLoopUtilization() 相同的调用,除了返回 worker 实例的值。

一个区别是,与主线程不同,工作线程内的引导是在事件循环内完成的。 因此,一旦工作线程的脚本开始执行,事件循环的利用率将立即可用。

不增加的 idle 时间并不表示工作线程卡在引导中。 下面的示例展示了工作线程的整个生命周期从未累积任何 idle 时间,但仍然能够处理消息。

const { Worker, isMainThread, parentPort } = require('node:worker_threads');

if (isMainThread) {
  const worker = new Worker(__filename);
  setInterval(() => {
    worker.postMessage('hi');
    console.log(worker.performance.eventLoopUtilization());
  }, 100).unref();
  return;
}

parentPort.on('message', () => console.log('msg')).unref();
(function r(n) {
  if (--n < 0) return;
  const t = Date.now();
  while (Date.now() - t < 300);
  setImmediate(r, n);
})(10); 

worker 的事件循环利用率仅在 'online' 事件 触发后可用,如果在此之前或 'exit' 事件 之后调用,则所有属性都具有 0 的值。

worker.postMessage(value[, transferList])#

向通过 require('node:worker_threads').parentPort.on('message') 接收到的工作线程发送消息。 有关详细信息,请参阅 port.postMessage()

worker.ref()#

unref() 相反,如果它是唯一剩下的活动句柄(默认行为),则在之前 unref()ed worker 上调用 ref() 不会让程序退出。 如果工作线程是 ref() 的,则再次调用 ref() 没有效果。

worker.resourceLimits#

为此工作线程提供了一组 JS 引擎资源约束。 如果将 resourceLimits 选项传给 Worker 构造函数,则这与其值匹配。

如果工作线程已经停止,则返回值是空对象。

worker.stderr#

这是包含工作线程内写入 process.stderr 的数据的可读流。 如果 stderr: true 没有传给 Worker 构造函数,则数据将通过管道传输到父线程的 process.stderr 流。

worker.stdin#

如果将 stdin: true 传给 Worker 构造函数,则这是可写流。 写入此流的数据将在工作线程中作为 process.stdin 可用。

worker.stdout#

这是包含工作线程内写入 process.stdout 的数据的可读流。 如果 stdout: true 没有传给 Worker 构造函数,则数据将通过管道传输到父线程的 process.stdout 流。

worker.terminate()#

尽快停止工作线程中的所有 JavaScript 执行。 返回在触发 'exit' 事件 时完成的退出代码的 Promise。

worker.threadId#

引用线程的整数标识符。 在工作线程内部,它作为 require('node:worker_threads').threadId 可用。 此值对于单个进程中的每个 Worker 实例都是唯一的。

worker.unref()#

如果这是事件系统中唯一的活动句柄,则在工作线程上调用 unref() 允许线程退出。 如果工作线程已经 unref(),则再次调用 unref() 无效。

注意事项#

stdio 的同步阻塞#

Worker 利用通过 <MessagePort> 传递的消息来实现与 stdio 的交互。 这意味着来自 Workerstdio 输出可能会被接收端的同步代码阻塞,这会阻塞 Node.js 事件循环。

import {
  Worker,
  isMainThread,
} from 'worker_threads';

if (isMainThread) {
  new Worker(new URL(import.meta.url));
  for (let n = 0; n < 1e10; n++) {
    // Looping to simulate work.
  }
} else {
  // This output will be blocked by the for loop in the main thread.
  console.log('foo');
}'use strict';

const {
  Worker,
  isMainThread,
} = require('node:worker_threads');

if (isMainThread) {
  new Worker(__filename);
  for (let n = 0; n < 1e10; n++) {
    // Looping to simulate work.
  }
} else {
  // This output will be blocked by the for loop in the main thread.
  console.log('foo');
}

从预加载脚本启动工作线程#

从预加载脚本(使用 -r 命令行标志加载和运行的脚本)启动工作线程时要小心。 除非显式设置了 execArgv 选项,否则新的工作线程会自动从正在运行的进程继承命令行标志,并将预加载与主线程相同的预加载脚本。 如果预加载脚本无条件地启动工作线程,则每个衍生的线程都会衍生另一个直到应用程序崩溃。