Node.js 中的多线程方案 Worker threads

源代码: lib/worker_threads.js

node:worker_threads 模块允许使用多线程并行执行JS代码。快速引用如下:

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

Workers (线程,下面不再注释) 在处理一些CPU密集型操作上非常有用。 但是对IO密集型操作则不适用。这是因为Node.js自带的IO多线程异步(Event loop)能力已经比 Workers 要更加强大。

不同于child_process(子线程) 或者 cluster(集群), 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() 调用部署了一个 Worker thread 去做 parse()逻辑。在实际情况当中,代码逻辑应当使用 Worker 池来缓存起来Worker,而不是每次调用都进行创建。否则,过度创建Worker的负面代价,就有可能超过Worker的使用带来的好处了。

在写一个Worker池时,使用 AsyncResource API 来通知诊断工具 (e.g. 提供一个异步调用的堆栈) 有关于任务和产出结果有关的相关性。 参考"在Worker thread 池中使用 AsyncResource" 里的 async_hooks 部分文档,里面有一个相关的实现实例。

Worker threads 默认继承无进程指定(non-process-specific)选项。参考 Worker 构建函数选项 来了解如何定制Worker跟线程的特性,特别是 argv 和 execArgv 选项。

worker.getEnvironmentData(key)

  • key  所有任意的、可复制的JS值,用法类似一个  key.
  • Returns: 

在一个 worker thread 内, 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

  • Returns: 

如果不是在一个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是不可在Worker间传输的。如果被标记的Object出现在 port.postMessage() 的传输列表中,它将会被忽略掉(此处存疑,可能是赋值而不是直接传输的意思)。

特别是,这对一些在发送端只能被复制而不是直接传送走的Objects是有用的。 举个例子, Node.js在自己的  Buffer pool  中用这个标记了 ArrayBuffers。

此操作不可回退。

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,只有nodejs后端中有。

后面都是一些api,再参考:Worker threads | Node.js v18.1.0 Documentation

你可能感兴趣的:(杂七杂八笔记,node.js,p2p,网络协议)