源代码: 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
在一个 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
如果不是在一个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 中用这个标记了 ArrayBuffer
s。
此操作不可回退。
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