如何使用perl将变量传递给子例程函数?

问题描述 投票:1回答:2

我的查询:

在下面的代码中,我试图将print $ commandoutput [0]移动或传递到即将到来的子例程中。我尝试了转移它。但是我失败了。可以请你帮我正确的方法?

码:

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];
Here i had tried to store the print $commandoutput[0] in the variable inside the subroutine.I gated here how to pass the variables from outside to inside the subroutine.
sub gen_help_data
{
  my $lines=shift;
  print $lines;
}
perl
2个回答
1
投票

startfinish之间的代码在一个单独的进程中运行,父和子不能访问彼此的数据(即使它们的变量碰巧具有相同的名称,它们也是不同的)。这是关于分叉的第一件事,它使用自己的数据创建一个单独的进程。

但是这个模块确实提供了一种机制,您可以通过该机制从子节点传回数据。请参阅docs中的Retrieving data structures from child processes

您首先需要向finish提供对孩子想要返回的数据结构的引用。在你的情况下,你想要返回一个标量$commandoutput[0]这样做

$fork->finish(0, \$commandoutput[0]);

然后在回调中找到此引用作为last,six,参数。你的代码遗漏了一个。所以在你需要的回调中

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],它存储在%ret_data中作为进程id的键的值。所以在foreach完成后你可以找到%ret_data中的所有数据

foreach my $pid (keys %ret_data) {
    say "Data from $pid => ${$ret_data{$pid}}";
}

在这里,我们将$ret_data{$pid}取消引用作为标量引用,因为您的代码返回了它。

请注意,数据是通过写出文件来传递的,如果正在进行,可能会很慢。


下面是一个完整的示例,其中每个子进程返回一个数组引用,方法是将其传递给finish,然后在回调中检索该引用。有关其他示例,请参阅this post

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

2
投票

我认为你误解了叉子的作用。当您成功分叉时,您将创建一个独立于您开始的流程的子流程,以继续工作。因为它是一个单独的进程,所以它有自己的内存,变量等,尽管其中一些最初是从父进程开始的。

所以你要在每个子进程中设置$commandoutput[0],但是当子进程终止时,@commandoutput副本的内容也会被删除。

您可以串行运行每个命令,也可以使用线程(其中包含许多其他问题 - 您的代码需要进行一些重要的重新设计才能使用线程),或者您可以使用事件(POE,AnyEvent等,这将是另一个重要的重新设计)。或者你可以运行每个命令,并将其输出放入临时文件中,然后,一旦完成所有子项,读取每个文件并继续。这也带来了问题,但问题通常比其他问题少。

© www.soinside.com 2019 - 2024. All rights reserved.