如何从子进程传递变量(由 Parallel::ForkManager 分叉)?
How to pass a variable from a child process (fork by Parallel::ForkManager)?
我的查询:
在下面的代码中,我试图将打印 $commandoutput[0]
转移或传递到即将到来的 subroutine.i 尝试转移以传递 it.But 我失败了 it.Can请你帮我找到正确的方法?
代码:
my $max_forks = 4;
#createThreads();
my %commandData;
my @arr = (
'bhappy', 'bload -m all -l -res CPUSTEAL',
'bqueues', 'bjobs -u all -l -hfreq 101'
);
#print @arr;
my $fork = new Parallel::ForkManager($max_forks);
$fork->run_on_start(
sub {
my $pid = shift;
}
);
$fork->run_on_finish(
sub {
my ( $pid, $exit, $ident, $signal, $core ) = @_;
if ($core) {
print "PID $pid core dumped.\n";
}
else { }
}
);
my @Commandoutput;
my $commandposition = 0;
for my $command (@arr) {
$fork->start and next;
my @var = split( " ", $command );
$commandoutput[$commandposition] = `$command`;
$commandposition++;
$line = $commandoutput[0];
# print $line;
$fork->finish;
}
$fork->wait_all_children;
#print Dumper(\%commandData);
print $commandoutput[0];
这里我曾尝试将打印 $commandoutput[0]
存储在 subroutine.I 内的变量中 gated here 如何将变量从外部传递到子例程内部。
sub gen_help_data
{
my $lines=shift;
print $lines;
}
我认为您误解了叉子的作用。当您成功分叉时,您正在创建一个子流程,独立于您开始的流程,以继续工作。因为它是一个单独的进程,所以它有自己的内存、变量等,尽管其中一些是从父进程开始的副本。
因此您在每个子进程中设置 $commandoutput[0]
,但是当该子进程终止时,其 @commandoutput
.
副本的内容也会终止
您可以 运行 每个命令连续执行,或者您可以使用线程(这会带来许多其他问题 - 您的代码需要进行一些重大的重新设计才能与线程一起工作),或者您可以使用事件(POE、AnyEvent 等,这将是另一个重大的重新设计)。或者您可以 运行 每个命令及其输出放入临时文件,然后,一旦所有子项都完成,读取每个文件并继续。这也有问题,但通常比其他问题少。
start
和 finish
之间的代码在 单独的进程中运行 并且 child 和 parent 无法写入彼此的变量(即使具有相同的名称)。 Forking创建了一个独立的进程,拥有自己的内存和数据。†为了在这些进程之间传递数据,我们需要使用"Inter-Process-Communication"(IPC)机制。
此模块确实提供了一种现成且简单的方法来将数据从 child 传回 parent。
请参阅文档中的 Retrieving data structures from child processes。
您首先需要向 finish
提供对 child 想要 return 的数据结构的引用。在你的情况下,你想要 return 一个标量 $commandoutput[0]
所以
$fork->finish(0, $commandoutput[0]);
然后在回调中找到此引用作为最后一个、第六个参数。您的代码遗漏的那个。所以在回调中你需要
my %ret_data; # to store data from different child processes
$pm->run_on_finish(
sub {
my ($pid, $exit, $ident, $signal, $core, $dataref) = @_;
$ret_data{$pid} = $dataref;
}
);
这里的$dataref
是$commandoutput[0]
,作为进程id的键的值存储在%ret_data
中。所以在 foreach
完成后你可以在 %ret_data
中找到所有数据
foreach my $pid (keys %ret_data) {
say "Data from $pid => ${$ret_data{$pid}}";
}
这里我们取消引用 $ret_data{$pid}
作为标量引用,因为您的代码 return 就是这样。
请注意,数据是通过写出文件传递的,如果有很多事情发生,这可能会很慢。
这是一个完整的示例,其中每个 child return 都是一个数组引用,将其传递给 finish
,然后在回调中检索。另一个例子见 .
use warnings;
use strict;
use feature 'say';
use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new(4);
my %ret_data;
$pm->run_on_finish( sub {
my ($pid, $exit, $ident, $signal, $core, $dataref) = @_;
$ret_data{$pid} = $dataref;
});
foreach my $i (1..8)
{
$pm->start and next;
my $ref = run_job($i);
$pm->finish(0, $ref);
}
$pm->wait_all_children;
foreach my $pid (keys %ret_data) {
say "$pid returned: @{$ret_data{$pid}}";
}
sub run_job {
my ($i) = @_;
return [ 1..$i ]; # make up return data: arrayref with list 1..$i
}
版画
15037 returned: 1 2 3 4 5 6 7
15031 returned: 1 2
15033 returned: 1 2 3 4
15036 returned: 1 2 3 4 5 6
15035 returned: 1 2 3 4 5
15038 returned: 1 2 3 4 5 6 7 8
15032 returned: 1 2 3
15030 returned: 1
† 在现代系统中,出于性能原因,在分叉新进程时尽可能少地复制数据。因此 child "inherits" 通过分叉产生的变量实际上并不是副本,因此 child 实际上读取了 parent 分叉时存在的变量。
但是,child 在内存中写入 的任何数据对于 parent 是不可访问的(并且 parent 在分叉后写入的是child)不知道。如果该数据在分叉 时从 parent 写入变量 "inherited",则发生 数据复制,以便 child 的新数据是独立的。
数据的管理方式肯定存在细微差别和复杂性,即使 child 中的数据发生变化,也只有 apparent 维护了一些指针。我猜这主要是为了简化数据管理,减少复制;数据管理的粒度似乎比 "variable" 级别细得多。
但这些是实现细节,一般来说 child 和 parent 不能互相查看对方的数据。
我的查询:
在下面的代码中,我试图将打印 $commandoutput[0]
转移或传递到即将到来的 subroutine.i 尝试转移以传递 it.But 我失败了 it.Can请你帮我找到正确的方法?
代码:
my $max_forks = 4;
#createThreads();
my %commandData;
my @arr = (
'bhappy', 'bload -m all -l -res CPUSTEAL',
'bqueues', 'bjobs -u all -l -hfreq 101'
);
#print @arr;
my $fork = new Parallel::ForkManager($max_forks);
$fork->run_on_start(
sub {
my $pid = shift;
}
);
$fork->run_on_finish(
sub {
my ( $pid, $exit, $ident, $signal, $core ) = @_;
if ($core) {
print "PID $pid core dumped.\n";
}
else { }
}
);
my @Commandoutput;
my $commandposition = 0;
for my $command (@arr) {
$fork->start and next;
my @var = split( " ", $command );
$commandoutput[$commandposition] = `$command`;
$commandposition++;
$line = $commandoutput[0];
# print $line;
$fork->finish;
}
$fork->wait_all_children;
#print Dumper(\%commandData);
print $commandoutput[0];
这里我曾尝试将打印 $commandoutput[0]
存储在 subroutine.I 内的变量中 gated here 如何将变量从外部传递到子例程内部。
sub gen_help_data
{
my $lines=shift;
print $lines;
}
我认为您误解了叉子的作用。当您成功分叉时,您正在创建一个子流程,独立于您开始的流程,以继续工作。因为它是一个单独的进程,所以它有自己的内存、变量等,尽管其中一些是从父进程开始的副本。
因此您在每个子进程中设置 $commandoutput[0]
,但是当该子进程终止时,其 @commandoutput
.
您可以 运行 每个命令连续执行,或者您可以使用线程(这会带来许多其他问题 - 您的代码需要进行一些重大的重新设计才能与线程一起工作),或者您可以使用事件(POE、AnyEvent 等,这将是另一个重大的重新设计)。或者您可以 运行 每个命令及其输出放入临时文件,然后,一旦所有子项都完成,读取每个文件并继续。这也有问题,但通常比其他问题少。
start
和 finish
之间的代码在 单独的进程中运行 并且 child 和 parent 无法写入彼此的变量(即使具有相同的名称)。 Forking创建了一个独立的进程,拥有自己的内存和数据。†为了在这些进程之间传递数据,我们需要使用"Inter-Process-Communication"(IPC)机制。
此模块确实提供了一种现成且简单的方法来将数据从 child 传回 parent。 请参阅文档中的 Retrieving data structures from child processes。
您首先需要向 finish
提供对 child 想要 return 的数据结构的引用。在你的情况下,你想要 return 一个标量 $commandoutput[0]
所以
$fork->finish(0, $commandoutput[0]);
然后在回调中找到此引用作为最后一个、第六个参数。您的代码遗漏的那个。所以在回调中你需要
my %ret_data; # to store data from different child processes
$pm->run_on_finish(
sub {
my ($pid, $exit, $ident, $signal, $core, $dataref) = @_;
$ret_data{$pid} = $dataref;
}
);
这里的$dataref
是$commandoutput[0]
,作为进程id的键的值存储在%ret_data
中。所以在 foreach
完成后你可以在 %ret_data
foreach my $pid (keys %ret_data) {
say "Data from $pid => ${$ret_data{$pid}}";
}
这里我们取消引用 $ret_data{$pid}
作为标量引用,因为您的代码 return 就是这样。
请注意,数据是通过写出文件传递的,如果有很多事情发生,这可能会很慢。
这是一个完整的示例,其中每个 child return 都是一个数组引用,将其传递给 finish
,然后在回调中检索。另一个例子见
use warnings;
use strict;
use feature 'say';
use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new(4);
my %ret_data;
$pm->run_on_finish( sub {
my ($pid, $exit, $ident, $signal, $core, $dataref) = @_;
$ret_data{$pid} = $dataref;
});
foreach my $i (1..8)
{
$pm->start and next;
my $ref = run_job($i);
$pm->finish(0, $ref);
}
$pm->wait_all_children;
foreach my $pid (keys %ret_data) {
say "$pid returned: @{$ret_data{$pid}}";
}
sub run_job {
my ($i) = @_;
return [ 1..$i ]; # make up return data: arrayref with list 1..$i
}
版画
15037 returned: 1 2 3 4 5 6 7 15031 returned: 1 2 15033 returned: 1 2 3 4 15036 returned: 1 2 3 4 5 6 15035 returned: 1 2 3 4 5 15038 returned: 1 2 3 4 5 6 7 8 15032 returned: 1 2 3 15030 returned: 1
† 在现代系统中,出于性能原因,在分叉新进程时尽可能少地复制数据。因此 child "inherits" 通过分叉产生的变量实际上并不是副本,因此 child 实际上读取了 parent 分叉时存在的变量。
但是,child 在内存中写入 的任何数据对于 parent 是不可访问的(并且 parent 在分叉后写入的是child)不知道。如果该数据在分叉 时从 parent 写入变量 "inherited",则发生 数据复制,以便 child 的新数据是独立的。
数据的管理方式肯定存在细微差别和复杂性,即使 child 中的数据发生变化,也只有 apparent 维护了一些指针。我猜这主要是为了简化数据管理,减少复制;数据管理的粒度似乎比 "variable" 级别细得多。
但这些是实现细节,一般来说 child 和 parent 不能互相查看对方的数据。