You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js中如何在主线程与Worker线程间共享大变量?

Node.js主线程与Worker线程共享大JSON数组的高效方案(基于Piscina)

针对你遇到的100+MB JSON数组无法在主线程与Worker线程高效共享的问题,以下两种方案可以避免重复克隆或重复读取文件的性能损耗:

方案一:使用SharedArrayBuffer实现内存共享

通过将JSON数据序列化为二进制格式存入SharedArrayBuffer,可以让主线程和所有Worker线程共享同一块内存区域,彻底避免数据克隆带来的开销。

修改后的代码:

main.js

const piscina = require('piscina');
const { resolve } = require('path');
const fs = require('fs');

// 读取并序列化大JSON文件为二进制
const jsonStr = fs.readFileSync('./heavy-file.json', 'utf8');
const encoder = new TextEncoder();
const uint8Array = encoder.encode(jsonStr);
// 创建SharedArrayBuffer存储二进制数据
const sharedBuffer = new SharedArrayBuffer(uint8Array.byteLength);
const sharedUint8Array = new Uint8Array(sharedBuffer);
sharedUint8Array.set(uint8Array);

async function runTasks() {
    const argLst = [1, 2, 3];
    const pool = new piscina({
        filename: resolve(__dirname, 'worker.js'),
        // 将SharedArrayBuffer作为workerData传递给所有Worker
        workerData: sharedBuffer
    });

    const requests = [];
    for (let arg of argLst) {
        requests.push(pool.run(arg));
    }
    await Promise.all(requests);
}

runTasks();

worker.js

const { workerData } = require('worker_threads');

async function task(arg) {
    // 从SharedArrayBuffer反序列化出JSON对象
    const decoder = new TextDecoder();
    const jsonStr = decoder.decode(new Uint8Array(workerData));
    const heavyVar = JSON.parse(jsonStr);

    let result = heavyVar.filter(r => r.prop == arg);
    console.log(result);
    return result;
}

module.exports = task;

方案二:利用Worker模块初始化特性,每个Worker仅加载一次文件

Piscina的每个Worker线程会独立加载模块文件,我们可以在Worker模块的顶层(而非任务函数内)读取JSON文件,这样每个Worker线程只会在启动时读取一次文件,后续任务直接复用已加载的内存数据,避免重复读取或传递的开销。

修改后的代码:

main.js

const piscina = require('piscina');
const { resolve } = require('path');

async function runTasks() {
    const argLst = [1, 2, 3];
    const pool = new piscina({
        filename: resolve(__dirname, 'worker.js')
    });

    const requests = [];
    for (let arg of argLst) {
        requests.push(pool.run(arg));
    }
    await Promise.all(requests);
}

runTasks();

worker.js

// 在Worker模块顶层加载JSON,每个Worker仅加载一次
const heavyVar = require('./heavy-file.json');

async function task(arg) {
    let result = heavyVar.filter(r => r.prop == arg);
    console.log(result);
    return result;
}

module.exports = task;

方案对比

  • 方案一:内存占用最低(仅一份数据),适合超大文件或线程池数量较多的场景;但需要额外的序列化/反序列化步骤,有少量性能开销。
  • 方案二:实现最简单,无需额外转换逻辑;但每个Worker线程会保留一份数据副本,内存占用为线程池大小 × 文件大小,适合线程池规模较小的场景。

内容的提问来源于stack exchange,提问作者sprash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 20:30:38