Node.js将带有函数定义的对象发送到工作线程

10

我正在使用Node.js开发一个项目,想要打开一些额外的线程以更有效地处理负载。但是我使用带有函数定义的类,并且在尝试将这些对象发送到工作线程时,对象中定义的函数会消失,只剩下对象中的其他字段。是否有办法发送一个能够保留函数并在工作线程中被调用的对象给工作线程?

var cluster = require('cluster');

if(cluster.isMaster){
    Monster = function(species){
        this.attack = function(){
            console.log('CHOMP');
        };
        this.name = species;
    };
    var vamp = new Monster('vampire'),
    worker   = cluster.fork();
    worker.send({'monster' : vamp});
    }
else{
    process.on('message', function(msg) {
        console.log(msg.monster); //this logs "{ name: 'vampire' }"  
        msg.monster.attack(); //TypeError: Object #<Object> has no method 'attack'
    });
}

1
困惑:你的问题标题和内容对我来说很有趣,也很合理,尤其是在2021年。但是代码示例与之无关/过时,因为它展示了如何使用nodejs进程集群而不是nodejs工作线程。即使我意识到在2012年nodejs并没有包含工作线程。 - Giorgio Robino
3个回答

6

不,无法在线程之间传递函数。您只能传递JS普通对象(仅数据),并使用当前线程中定义的函数处理它(例如使用接收到的数据创建新对象)。


2
哇,那真的限制了集群模块的功能。你知道是否有计划将其添加到Node.js中吗? - Charlie Lipford
1
这个功能与node.js的架构相违背。Node.js事件循环在单线程中工作。集群模块只是启动新进程。它们都有自己独立的内存。实现多线程架构需要支持具有共享内存和相关机制(如“互斥锁”和“信号量”)的线程。这不是node.js的方式。 - Vadim Baryshev
已赞。有关函数为什么不能在线程之间共享的详细信息,请参见以下链接:1. https://nodejs.org/api/worker_threads.html#worker_threads_worker_workerdata 和 2. https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm。 - Giorgio Robino

3

Charlie,我知道你一年前就提出了这个问题,但我也想做类似的事情,于是我看到了你的问题,发现你还没有标记答案。我想试着回答一下,并展示一下我对你的代码所做的工作。这种不同的代码组织方式对我来说是一个非常可接受的解决方法,适用于我的node.js工作。我相信这可以给你提供一种实现你想要的方式,即使你不能按照你想要的方式去做。

将你的“类”声明在集群代码之外,就像这样:

var cluster = require('cluster');

var Monster = function(species){
    this.attack = function(){
        console.log('CHOMP!');
    };
    this.die = function() {
        console.log("Oh, what did I eat?  I don't feel so good....\r\n");
        process.exit(0);
    };
    this.scare = function() {
        console.log("BOO! I am a " + this.name + "!");
    };
    this.name = species;
};

if(cluster.isMaster){
    worker = cluster.fork();
    worker.send({'species' : 'Vampire'});
}
else{
    process.on('message', function(msg) {
        if(typeof msg.species !== "undefined") {
            myMonster = new Monster(msg.species);
            myMonster.scare();
            myMonster.attack();
            myMonster.die();
        }
    });
}

尝试一下,看看这是否是您可以接受的答案!


聪明的解决方案,也许是唯一可行的方案。但是在你的代码中,没有共享“带有函数的对象到工作线程”,正如问题标题所述。没有工作线程,你只是分叉了一组进程,每个进程创建自己的对象。这很好,但是如果你想共享大型数据对象和/或包含函数(引用),那么这不是最佳选择。 - Giorgio Robino

0

好的,偶然发现了这个答案,我觉得很奇怪没有人提到这一点,但它可能是一个比问题更现代的特性:

eval

let str = "() => { console.log('test') }"
let func = eval(str)
func()

我认为这里正在发生的事情很明显,您可以将任何字符串解析为JavaScript,并且可以将字符串发送到工作线程,因此您可以构建具有函数的对象:

let obj = { a: "() => { ... }" }

并将对象发送过去。(首先使用 JSON.stringify(obj),然后您将需要先解析对象,然后分别解析所有子字符串)


2
这是潜在的危险行为,特别是在具有访问系统其他部分权限的Node服务器上。想象一下,有人上传了一个名为return (function () { /* arbitrary code*/ })();的文件,并且在您的类中,该文件名以某种方式被传递给了eval而不是您的函数。虽然可能性很小,但在服务器代码中使用eval只会为您的系统带来更多漏洞。 - Austin Davis
如果你要评估一个请求,我完全同意。然而,在这个 OP 的情况下,他正在将自己的代码发送给工作者,所以在我看来,“eval” 是最好的解决方案。 - Craig O'Connor
2
“eval” 说实话是最好的解决方案,除非你知道你可以只序列化函数。老实说,如果所讨论的函数不是执行计算的函数,将其封装为一个回调监听其自己的个性化“postMessage”实际上是最好的解决方案(额外的好处是任何闭包和类实例都可以继续工作;因为它们从未离开原始线程)。 - Hashbrown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接