[node.js]向线程传递参数的方法

为了实现异步操作,我学习了线程的创建方法,但关于参数传递的方式没有太多信息,所以我把它记录下来。

在 JavaScript 中,我不确定是否合适称之为“线程”
(基本上它是以单线程方式运行的,因此其具体机制仍然不明确)
但是在尝试运行时,它似乎是以异步的方式运行的。

环境

macOS: 十点十五点五版卡塔琳娜
node.js: v14.3.0

关于线程调用类型的选择

非同步線程的創建方式有兩種:①群集②子進程調用。下面簡單介紹每種的用法。
① 是指源代碼(js文件)同時執行多個的情況
② 是指與源代碼不同的js文件同時執行的情況。

调用集群

var cluster = require('cluster');
if (cluster.isMaster)   { console.log('parent'); cluster.fork();    }
else                    { console.log('child');                     }

上述代码的执行结果如下所示。

parent
child

在这种情况下,我尝试了但无法将参数传递给每个(子)线程。
在功能上,如果有多个完全相同的事件处理程序,性能可能会提高,但是我对此还没有了解。

调用子进程

以下是一个示例步骤:
要传递参数,请将array作为fork()的第二个参数传递。
child_process.fork(要调用的JS文件名, 参数数组);

var child_process = require('child_process');
var c = child_process.fork(__dirname+'/child' , ['foo' , 'bar' ] );

被称为子进程的进程会将从父进程传递的参数存储在process.argv数组中。
以下示例将打印出所有参数元素。

process.argv.forEach( function(item) {console.log("arg:" + item);} );   //dump args

执行方式和结果如下。

$ node parent
arg:/Users/***/.nodebrew/node/v14.3.0/bin/node
arg:/Users/***/***/child
arg:foo
arg:bar

process.argv[0] : node executable file path
process.argv[1] : path of the executed js file
process.argv[2] : argument array element [0]: this time the parent process specifies “foo”.
process.argv[3] : argument array element [1]: this time the parent process specifies “bar”.

process.argv[0]:node 可执行文件路径
process.argv[1]:执行的 js 文件路径
process.argv[2]:参数数组元素 [0]:这次父进程指定了 “foo”。
process.argv[3]:参数数组元素 [1]:这次父进程指定了 “bar”。

通过 process.argv[2] 以及后续的读取方式来实际参考参数。
借助这种调用方法,可以通过父线程指示每个线程的工作内容。

家庭成员间的互通

在子进程的情况下,父子进程可以进行消息交互。两者都有on()作为接收处理器(参数是消息内容),send()作为发送处理器。

var child_process = require('child_process');
var c = child_process.fork(__dirname+'/child' , ['foo' , 'bar' ] );

c.on('message', function (msg) { console.log('[Parent]received msg = [' + msg + ']'); }); //msg handler
c.send('Hello');
process.on('message', function(msg) {
        console.log('[Child]received msg = [' + msg + ']' );
        if(msg == 'Hello') process.send('Hello by ' + process.argv[2]);
})

执行结果如下。

$ node parent
[Child]received msg = [Hello]
[Parent]received msg = [Hello by foo]

为了试验,我尝试调用了许多子进程。

这只是一个参考,但我试着多次调用来看看会发生什么。

var child_process = require('child_process');
const n_child = 16;
var _childlen =[];
for( var i = 0 ; i < n_child ; ++i) {
        _childlen[i] = child_process.fork(__dirname+'/child' , [ i ] );
        _childlen[i].on('message', function (msg) { console.log('[Parent]received msg = ['+ msg + ']' ); });
}
_childlen.forEach( function(a) { a.send('Hello'); });
process.on('message', function(msg) {
        console.log('[Child]received msg = [' + msg + ']' );
        if(msg == 'Hello') process.send('Hello by ' + process.argv[2]);
})

实际执行的结果如下:
可以看出父进程的send()操作和子进程的回复操作是异步(同时)执行的。

收到来自child的消息时,父进程的dump([Parent] received)是无序的,这很有意思。
这是由内部处理来确定事件处理程序的顺序(仅是猜测)。
在父进程的事件处理等待队列中,它们被认为是无序的,
这意味着child的发送处理(共16个)是异步进行的。
简单地考虑,如果每个子进程都作为单独的线程运行,效率会相当高。
如果遇到性能问题,可以考虑使用这种方式来进行并行处理等手段。

$node parent
Child]received msg = [Hello]
[Parent]received msg = [Hello by 0]
[Child]received msg = [Hello]
[Child]received msg = [Hello]
[Child]received msg = [Hello]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 3]
[Parent]received msg = [Hello by 5]
[Parent]received msg = [Hello by 6]
[Parent]received msg = [Hello by 1]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 4]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 2]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 7]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 11]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 8]
[Child]received msg = [Hello]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 10]
[Parent]received msg = [Hello by 12]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 9]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 14]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 13]
[Child]received msg = [Hello]
[Parent]received msg = [Hello by 15]

查看操作系统的进程,确实发现每个子进程(线程)都生成了一个进程(线程)。

6049 s000  S+     0:00.01 grep node
 6023 s002  S+     0:00.20 node parent
 6024 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 0
 6025 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 1
 6026 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 2
 6027 s002  S+     0:00.11 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 3
 6028 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 4
 6029 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 5
 6030 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 6
 6031 s002  S+     0:00.11 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 7
 6032 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 8
 6033 s002  S+     0:00.09 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 9
 6034 s002  S+     0:00.09 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 10
 6035 s002  S+     0:00.09 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 11
 6036 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 12
 6037 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 13
 6038 s002  S+     0:00.10 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 14
 6039 s002  S+     0:00.09 /Users/***/.nodebrew/node/v14.3.0/bin/node /Users/***/webserver_nodejs/child 15

我试着叫了更多人。

我尝试将上述代码中的子进程数量增加。

   :
const n_child = 4096;
   :

The outcome is as follows.

$node parent
child : 0
child : 1
    :
    :
child :385
child :405

(libuv) kqueue(): Too many open files in system
/Users/***/.nodebrew/node/v14.3.0/bin/node[7040]: ../src/tracing/agent.cc:55:node::tracing::Agent::Agent(): Assertion `(uv_loop_init(&tracing_loop_)) == (0)' failed.
     :
     :
Error: ENFILE: file table overflow, uv_pipe_open
      :
SystemErr

当子进程数量超过400个时,出现了错误提示。
我本以为是操作系统的线程数达到了极限,但实际上是由于node.js系统的libuv(负责处理IO相关操作的部分)处理管道数量达到了极限。
我认为这是在父子进程之间通信所使用的。
在执行过程中,我所使用的浏览器也遇到了读取错误,所以我认为这对操作系统也造成了负担。
由于进程过多(每个进程都需要进行一定的初始化等处理),导致网络处理跟不上。但MacOS本身并未出现崩溃等问题。
虽然过多地进行调用是不好的,但也许通过延迟一段时间再启动子进程,问题就会消失。(以上都是猜测)

广告
将在 10 秒后关闭
bannerAds