【问题标题】:How to pass a variable from a child process (fork by Parallel::ForkManager)?如何从子进程(Parallel::ForkManager fork)传递变量?
【发布时间】:2017-06-12 23:00:31
【问题描述】:

我的查询:

在下面的代码中,我试图将打印 $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];

在这里,我尝试将打印 $commandoutput[0] 存储在子例程内的变量中。我在这里设置了如何将变量从外部传递到子例程内部。

sub gen_help_data
{
  my $lines=shift;
  print $lines;
}

【问题讨论】:

    标签: perl multiprocessing fork ipc child-process


    【解决方案1】:

    startfinish 之间的代码在单独的进程中运行,并且子和父不能写入彼此的变量(即使同名)。分叉创建一个具有自己的内存和数据的独立进程。要在这些进程之间传递数据,我们需要使用“进程间通信”(IPC)机制。

    这个模块确实提供了一种现成且简单的方法来将数据从子节点传回父节点。 请参阅文档中的 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 返回:1 2 3 4 5 6 7 15031 返回:1 2 15033 返回:1 2 3 4 15036 返回:1 2 3 4 5 6 15035 返回:1 2 3 4 5 15038 返回:1 2 3 4 5 6 7 8 15032 返回:1 2 3 15030 返回:1

    在现代系统上,由于性能原因,在新进程分叉时复制的数据尽可能少。因此,孩子通过分叉“继承”的变量实际上并不是副本,因此孩子实际上确实读取了在分叉时存在的父变量。

    但是,子写入内存中的任何数据对于父来说是不可访问的(并且在分叉之后父写入的内容对于子来说是未知的)。如果在分叉时将该数据写入从父级“继承”的变量然后,则会发生数据复制,以便子级的新数据是独立的。

    管理数据的方式肯定存在微妙之处和复杂性,即使子项中的数据发生变化,显然也会保留许多指针。我猜这主要是为了简化数据管理,减少复制;数据管理的粒度似乎比“可变”级别要精细得多。

    但这些都是实现细节,一般来说,孩子和父母不能互相戳对方的数据。

    【讨论】:

    • @examplefile 太好了——如果更多解释有用,请告诉我。
    【解决方案2】:

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

    因此,您在每个子进程中设置 $commandoutput[0],但是,当该子进程终止时,其 @commandoutput 副本的内容也会终止。

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

    【讨论】:

    • 如果需要数据共享,我非常喜欢使用 Thread::Queue Like This 进行“工作线程”样式的操作
    • @Sobrique 可以很好地从P::FM 的孩子那里返回数据,请参阅我的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    相关资源
    最近更新 更多