在本文中,我们讨论最新版本的Node.js(13.0)中Node和Worker Threads中并行性背后的基本概念。
并发与并行
长期以来,Node.js在并发方面表现出色。在最近发布的Node 13.0中,Node现在对并行性也有了稳定的答案。
并发可以看作是异步过程之间的切换,所有异步过程都轮流执行,并且在空闲时将控制权返回到事件循环。另一方面,并行性是进程分离并同时在多个线程上运行的能力。JavaScript中还有其他解决方案试图解决此问题。
主脚本
主脚本必须做三件事
- 通过引用工作程序的js文件来创建工作程序。
const worker = new WorkerThread.Worker(join(__dirname, './worker.js'))
- 向工人发送消息以启动工作。消息只是一个JavaScript对象,因此,如果您需要自定义工作程序的行为,则可以包括任何分类,数据等。
worker.postMessage({foo:"stuff"});
- 注册对动作的响应。工作者的任何响应都将是消息事件,并返回一个对象。您可以返回字符串或更复杂的对象,并带有工作人员已执行的任何操作的结果。
worker.on('message', (message) =>{ //对响应进行处理}
现在,尝试将它们组合成脚本
const { join } = require('path');const WorkerThread = require('worker_threads');const THREAD_COUNT = 30;/ ** *运行修改THREAD_COUNT之前 * /(async () => { //设置运行作业所需的任何数据。 const users = await getAllUsers(); //定义函数以处理工作人员传递的不同消息 function handleStatusReport(workerNumber, report) { console.log(`the worker:${workerNumber} says`, report.body || report); } function handleWorkerFinished(worker, workerNumber, message) { console.log(`done with ${JSON.stringify(message.body)}!`); if (i < users.length) { worker.postMessage(users[i]); i += 1; } else { console.log(`Worker number ${workerNumber} completed working!`); } } //提升第一批工作人员......再利用这些工人,让他们完成更多的工作 for (let j = 0; j < Math.min(THREAD_COUNT, users.length); j += 1) { const worker = new WorkerThread.Worker(join(__dirname, './worker.js')); console.log(`Running ${i} of ${users.length}`); worker.postMessage(users[i]); i += 1; //听工作者的消息 worker.on('message', (messageBody) => { //打开消息正文中的值,以支持不同类型的消息 if (messageBody.type === 'done') { handleWorkerFinished(worker, j, messageBody); } else { handleStatusReport(j, messageBody); } }); }})();
工作者脚本
可以简单到
parentPort.on('message', async someObject => //一些使用对象的函数,可以通过返回给父工作器报告);
原本不需要报告;但这是个好习惯,它可以知道工作人员是否完成,失败或有选择地返回计算结果。
parentPort.postMessage({ type: 'done', body: {key: 'value'} });
parentPort.postMessage({ type: 'log', body: {message: 'something happened'} });
上面的示例具有一个type值为 done和 的属性 log。名称和值都是任意的-请记住,可以通过这种方式在工人和主人之间交换数据。另外,我应该注意,从工作程序向主服务器发送消息不会终止工作程序。因此,一个工人可以在执行期间发送许多消息。
这是针对工人的更全面的模板:
const { parentPort } = require('worker_threads');async function process(someObject) { //在这里做很多处理... //将状态更新发送给PRIMARY parentPort.postMessage({ type: 'status', body: 'still working' }); //进行更多处理... //将带有相关数据的最终消息发送回PRIMARY脚本 parentPort.postMessage({ type: 'done', body: {key: 'value'} });}//注册方法以对消息执行parentPort.on('message', async
补充说明
线程工作器直到最近才成为Node 13.0中的稳定功能。如果在运行旧版本的节点时发生任何错误,则值得通过使用--experimental-worker标志运行来启用线程工作器。
node --experimental-worker master.js
希望能对你有用。