有用的内置Node.js APIs( 六 )


Worker Threads官方文档是这么说的:Workers(线程)对于执行CPU密集型的JavaScript操作很有用 。它们对I/O密集型的工作帮助不大 。Node.js内置的异步I/O操作比Workers的效率更高 。
假设一个用户可以在你的Express应用程序中触发一个复杂的、十秒钟的JavaScript计算 。该计算将成为一个瓶颈,使所有用户的处理程序停止 。你的应用程序不能处理任何请求或运行其他功能,除非它计算完成 。
异步计算处理来自文件或数据库数据的复杂计算可能问题不大,因为每个阶段在等待数据到达时都是异步运行 。数据处理发生在事件循环的不同迭代中 。
然而,仅用JavaScript编写的长运行计算,比如图像处理或机器学习算法,将占用事件循环的当前迭代 。
一种解决方案就是worker线程 。这类似于浏览器的web worker以及在独立线程上启动JavaScript进程 。主线程和worker线程可以交换信息来触发或者终止程序 。
Workers和事件循环Workers对CPU密集型JavaScript操作很有用,尽管Node.js的主事件循环仍应用于异步I/O活动 。
示例代码有一个worker项目,其在lib/dice.js中导出diceRun()函数 。这是将任意数量的N面骰子投掷若干次,并记录总分的计数(应该是正态分布曲线的结果):
// dice throwingexport function diceRun(runs = 1, dice = 2, sides = 6) {const stat = [];while (runs > 0) {let sum = 0;for (let d = dice; d > 0; d--) {sum += Math.floor( Math.random() * sides ) + 1;}stat[sum] = (stat[sum] || 0) + 1;runs--;}return stat;}index.js中的代码启动一个进程,每秒钟运行一次并输出一条信息:
// run process every secondconst timer = setInterval(() => {console.log('another process');}, 1000);调用diceRun()函数,将两个骰子抛出10亿次:
import { diceRun } from './lib/dice.js';// throw 2 dice 1 billion timesconstnumberOfDice = 2,runs = 999_999_999;const stat1 = diceRun(runs, numberOfDice);这将暂停计时器,因为Node.js事件循环在计算完成之前不能继续下一次迭代 。
然后,将上述代码在一个新的Worker中尝试相同的计算 。这会加载一个名为worker.js的脚本,并在配置对象上的workerData属性传递计算参数:
import { Worker } from 'worker_threads';const worker = new Worker('./worker.js', { workerData: { runs, numberOfDice } });事件处理器被附加到运行worker.js脚本的worker对象上,以便它能接收传入的结果:
// result returnedworker.on('message', result => {console.table(result);});以及处理错误:
// worker errorworker.on('error', e => {console.log(e);});以及在处理完成后进行整理:
// worker completeworker.on('exit', code => {// tidy up});worker.js脚本启动diceRun()计算,并在计算完成后向父脚本发布一条消息--该消息由上面的message处理器接收:
// worker threadimport { workerData, parentPort } from 'worker_threads';import { diceRun } from './lib/dice.js';// worker threadimport { workerData, parentPort } from 'worker_threads';import { diceRun } from './lib/dice.js';// start calculationconst stat = diceRun( workerData.runs, workerData.numberOfDice );// post message to parent scriptparentPort.postMessage( stat );worker运行时,计时器并没有暂停,因为它是在另一个CPU线程上执行的 。换句话说,Node.js的事件循环继续迭代,而没有长延迟 。
使用node index.js运行项目代码 。

有用的内置Node.js APIs

文章插图
你应该注意到了,基于

经验总结扩展阅读