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

时间:2017-01-27 09:47:11

标签: perl

我的查询:

在下面的代码中,我试图将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];
在这里,我试图将print $ commandoutput [0]存储在子例程内的变量中。我在这里指出如何将变量从外部传递到子例程内部。
sub gen_help_data
{
  my $lines=shift;
  print $lines;
}

2 个答案:

答案 0 :(得分:2)

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

所以你在每个子流程中设置$commandoutput[0],但是当子流程消失时,@commandoutput副本的内容也会消失。

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

答案 1 :(得分:1)

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

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

首先需要向finish提供对孩子想要返回的数据结构的引用。在您的情况下,您想要返回标量$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],其存储在%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