node 怎么实现并行化执行传输不可序列化对象?

176 天前
 nyxsonsleep

现在的需求是存在一个类内的方法,原来是串行的,现在需要改并行。现在我需要将一个对象 obj ,或者对象的方法 obj.run 传入子线程,然后回调执行。

但是我尝试了几种方式,似乎是没办法将复杂的对象进行传递?以至于常规的回调函数的方式没办法在 node 的并行化中实现。

  1. worker_thread
new Worker(moduleThreadFile, 
workerData:{'obj':obj}) //ERROR

会报告 Cannot set property code of which has only a getter.

  1. workerpool
pool=workerpool.pool()
pool.exec(obj,[])

实际上传入的 obj 在子线程中是 undefined

2805 次点击
所在节点    Node.js
51 条回复
sinalvee
176 天前
没实践过,不知道有没有别的坑,思路就是把函数转为字符串,worker 中再把字符串转回来

```js
const { Worker, isMainThread, parentPort, workerData } = require('node:worker_threads');

if (isMainThread) {
const obj = {
name: 'Foo',

greet(other) {
return `Hello ${this.name} and ${other}`;
}
}

const objStr = JSON.stringify(obj, (key, value) => {
if (typeof value === 'function') {
return value.toString();
}
return value;
});

const worker = new Worker(__filename, {
workerData: objStr,
});
worker.on('message', (value) => {
console.log('Receive data from worker =>', value);
});
worker.on('error', console.error);
worker.on('exit', (code) => {
if (code !== 0)
console.error(new Error(`Worker stopped with exit code ${code}`));
});
} else {
const objStr = workerData;
const objParsed = JSON.parse(objStr);

const run = (obj, funcName, ...args) => {
if (obj.hasOwnProperty(funcName)) {
const funcStr = obj[funcName];
// 提取函数体,忽略函数参数定义
const funcBody = funcStr.substring(obj.greet.indexOf('{') + 1, obj.greet.lastIndexOf('}'));
// 使用剩余参数语法来定义一个新的函数,允许接收任意数量的参数
const funcArgs = funcStr.substring(funcStr.indexOf('(') + 1, funcStr.indexOf(')')).split(',').map(arg => arg.trim()).filter(arg => arg);
const func = new Function(...funcArgs, funcBody);

return func.call(obj, ...args);
}
}

const result = run(objParsed, 'greet', 'Bar');

parentPort.postMessage(result);
}
```
nyxsonsleep
176 天前
@shadowyue #16 JSON.stringfy 试过,不行,函数丢失了。这个对象有很多静态成员和方法。
nyxsonsleep
176 天前
@yaodong0126 #20 首先这个工程的主要功能需要编译成一个单文件,由于某些原因可能不会轻易改变这种行为。
此外由于 node 的多线程需要一个单独的 js 文件作为入口,所以我创建了一个新工程,因此这个工程无法直接导入原工程的内容。所以我尝试进行回调。
Melting
176 天前
可以用 module.exports 导出方法,在 worker_threads 里调用吧
nyxsonsleep
176 天前
@Melting #24
> 首先这个工程的主要功能需要编译成一个单文件,由于某些原因可能不会轻易改变这种行为。
此外由于 node 的多线程需要一个单独的 js 文件作为入口,所以我创建了一个新工程,来创建新的 js 文件入口,因此这个工程无法直接导入原工程的内容。所以我尝试进行回调。
okakuyang
176 天前
worker 不能传函数,你可以一个任务给一个 id ,任务处理完传 id 给主线程,根据 id 执行对应回调。超级简单。
nyxsonsleep
176 天前
@okakuyang #26 没看明白。我就是需要将任务传递给 worker ,不然这个任务怎么处理完呢?子进程根本不知道应该做什么吧。
现在我需要并行化的就是 obj.run 函数,子进程中没有这个函数,也没有这个对象,怎么执行 obj.run 呢?
momocraft
176 天前
重写一份不绑定对象的,参数可以序列化的 run
EchoWhale
176 天前
别想了,自己实现一个序列化/反序列化方法吧。
nomagick
176 天前
你就别当 node 有线程,node 相当于没有线程,先序列化再反序列化,多进程模型

而且 node 里面的 fork, 也不是你认为的 fork, 纯就是重新再启动一个新的
nomagick
176 天前
函数和对象引用都是不共享的,也不能传递,只能通信
mark2025
176 天前
@nyxsonsleep 在 worker 内加载/初始化这个对象不行么?
okakuyang
176 天前
@nyxsonsleep 你的 worker 里面本身要有 [处理计算] 的代码,主线程只是负责把 [要处理的数据] [id] 传给 worker 线程,worker 计算完成之后只负责把计算好的数据 [字符串/基本类型/字节] [id] 发给主线程,主线程收到处理好的数据根据 [id] 再进行下一步处理 [合并数据] 。
nyxsonsleep
175 天前
@mark2025 #32 > 首先这个工程的主要功能需要编译成一个单文件,由于某些原因可能不会轻易改变这种行为。
此外由于 node 的多线程需要一个单独的 js 文件作为入口,所以我创建了一个新工程,来创建新的 js 文件入口,因此这个工程无法直接导入原工程的内容。

基于以上原因,实际上 worker 里基本没办法初始化这个对象。或者能提供某种方法可以避免上述的限制吗?
image72
175 天前
我看到 workerData 支持 Blob, dataview,arraybuffer 类型(The structured clone algorithm
)[https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#webapi_types]
mark2025
174 天前
@nyxsonsleep 能不能就这一个 js 文件既是主主入口文件也是 worker 入口文件,然后内部根据 mian/worker 分支判断呢?
nyxsonsleep
174 天前
@mark2025 #36 这个 js 文件有 10+M ,worker 子线程启动的时候会不会有性能问题。
yaodong0126
173 天前
@nyxsonsleep 你编不编译成单文件不重要啊,你都新建一个工程了,那么你的新工程存在两个文件就不可以?文件 A 作为和主交互的入口,并在里面 require 你的单文件 B 执行,B 执行完毕通过 A 把消息传递给主,不就完了吗,下面 26 楼说的也是这个意思
accelerator1
173 天前
nodejs 中的 worker 是多进程,不是多线程,不能内存共享,正常情况没法传递引用。
但是 worker 支持 transferable 对象,也就是可以直接传递引用避免进程间的数据拷贝,其实还是要自己实现序列/反序列化。

如果你的 obj 不可序列化,那就把 obj 的实例化函数放到 worker 中,通过传递 obj 实例化的相关参数来实现。
kyuuseiryuu
173 天前
源码级传递 —— 你在 worker 侧实现一个一摸一样的类。这样把数据丢过去就能计算了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1058599

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX