Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Perl 循环内部分叉防止迭代器使用Parallel::Prefork递增_Perl_Iterator_Fork - Fatal编程技术网

Perl 循环内部分叉防止迭代器使用Parallel::Prefork递增

Perl 循环内部分叉防止迭代器使用Parallel::Prefork递增,perl,iterator,fork,Perl,Iterator,Fork,我有一些代码,我希望输出是1和6,但它输出1个无穷大 use v5.10; use Parallel::Prefork; use List::MoreUtils qw( natatime ); use POSIX qw( ceil ); my $forks = 2; my @numbers = (1..10); my $chunk_size = ceil((scalar @numbers) / $forks); my $game_iterator = natatime $c

我有一些代码,我希望输出是1和6,但它输出1个无穷大

use v5.10;
use Parallel::Prefork;
use List::MoreUtils qw( natatime );
use POSIX qw( ceil );

my $forks = 2;

my @numbers       = (1..10);
my $chunk_size    = ceil((scalar @numbers) / $forks);
my $game_iterator = natatime $chunk_size, @numbers;
my $fm            = Parallel::Prefork->new({ max_workers => $forks });

while ($fm->signal_received ne 'TERM') {
  while( my @numbers_chunk = $game_iterator->() ) { 
    $fm->start(sub {
        say $numbers_chunk[0];
    });
  }
}

$fm->wait_all_children;

# bash-4.2$ perl test.pl
# 1
# 1
# 1
# 1
# 1
# etc
其中,上面的脚本将一个由10个数字组成的数组拆分为$fork number个数组(2),并将这些数组中的每个数组传递给它们自己的fork进行处理

如果替换
$fm->start(子{say$numbers_chunk[0];})只需
说出$numbers_chunk[0]显示正确的结果。Parallel::ForkManager还输出正确的结果(按照概要),因此我不知道我是否做了错误的事情,或者这是模块中的错误

输出预期结果的ForkManager脚本:

use v5.10;
use Parallel::ForkManager;
use List::MoreUtils qw( natatime );
use POSIX qw( ceil );

my $forks = 2;

my @numbers       = (1..10);
my $chunk_size    = ceil((scalar @numbers) / $forks);
my $game_iterator = natatime $chunk_size, @numbers;
my $fm            = Parallel::ForkManager->new($forks );

while( my @numbers_chunk = $game_iterator->() ) { 
  $fm->start and next;
  say $numbers_chunk[0];
  $fm->finish;
}

$fm->wait_all_children;


# bash-4.2$ perl test.pl
# 1
# 6
是为不需要来自父进程的数据的独立无状态、可重启的工作进程而设计的。该模块不提供将数据线程化到回调的功能,这使得设置通信通道(如传递数字块)非常困难

与下面直接调用
fork
的简单程序相比,该模块似乎没有给您带来任何好处

#! /usr/bin/env perl

use strict;
use warnings;

use v5.10;
use List::MoreUtils qw( natatime );
use POSIX qw( ceil WNOHANG );

my $forks = 2;

my @numbers       = (1 .. 10);
my $chunk_size    = ceil(scalar @numbers / $forks);
my $game_iterator = natatime $chunk_size, @numbers;

for (1 .. $forks) {
  if (my @numbers_chunk = $game_iterator->()) {
    unless (fork // die "$0: fork: $!") {
      say $numbers_chunk[0];
      exit 0;
    }
  }
}

# wait for all child processes
my $pid;
do { $pid = waitpid -1, WNOHANG } while $pid > 0;
您可以通过使用System V IPC绕过Parallel::Prefork的设计约束,例如,使用下面代码中的消息队列

#! /usr/bin/env perl

use strict;
use warnings;

use Parallel::Prefork;
use List::MoreUtils qw( natatime );
use POSIX qw( ceil );
use IPC::SysV qw(IPC_NOWAIT IPC_PRIVATE S_IRUSR S_IWUSR);
use IPC::Msg;
use Errno qw( ENOMSG );

my $forks = 3;

my @numbers       = (1 .. 20);
my $chunk_size    = ceil((scalar @numbers) / $forks);
my $game_iterator = natatime $chunk_size, @numbers;
my $fm            = Parallel::Prefork->new({ max_workers => $forks });

my $maxsize = 0;
my $msg = new IPC::Msg(IPC_PRIVATE, S_IRUSR | S_IWUSR);
while (my @numbers_chunk = $game_iterator->()) {
  my $chunk = join " ", @numbers_chunk;
  $msg->snd(1, $chunk) or die "$0: msgsnd: $!";
  $maxsize = length $chunk if length $chunk > $maxsize;
}

my $ppid = $$;

while ($fm->signal_received ne 'TERM') {
  $fm->start(sub {
    my $ok = $msg->rcv(my $buf, $maxsize, 1, IPC_NOWAIT);
    if (!$ok) {
      if ($!{ENOMSG}) {
        sleep 1;  # XXX: poor man's synchronization
        kill TERM => $ppid or die "$0: kill: $!";
        return;
      }
      die "$0: msgrcv: $!";
    }
    print "[$$]: got '$buf'\n";
  });
}

$fm->wait_all_children;
此实现是可以通过的,因为所有进程都使用相同的全局消息队列对象

样本输出:

[31198]: got '8 9 10 11 12 13 14' [31197]: got '1 2 3 4 5 6 7' [31200]: got '15 16 17 18 19 20' [31198]:得到'891011121314' [31197]:得到“1234567” [31200]:获得“1517181920”
正如上面的代码所示,您确实需要一个比Parallel::Prefork提供的更适合您的问题的抽象。

与文档相反,Parallel::Prefork与Parallel::ForkManager非常不同。它被设计用于web服务器之类的东西,它加载一次配置,然后生成相同的子项,直到被信号关闭

因此,
start
会根据需要不断创建子进程,直到捕获到终止整个进程的信号后才会返回

也就是说,通过在fork之前使用
,可以使p::Prefork像p::ForkManager的胖版本一样工作

use strict;
use warnings;
use v5.10;

use List::MoreUtils   qw( natatime );
use Parallel::Prefork qw( );
use POSIX             qw( ceil );

my $forks = 2;

my @numbers       = (1..10);
my $chunk_size    = ceil(@numbers / $forks);
my $game_iterator = natatime($chunk_size, @numbers);

my @numbers_chunk;

my $fm = Parallel::Prefork->new({
   max_workers => $forks,
   trap_signals => { TERM => 'TERM' },
   before_fork => sub {
      @numbers_chunk = $game_iterator->()
         or kill(TERM => $$);
   },
});

$fm->start(sub {
   say $numbers_chunk[0];
});

$fm->wait_all_children();

但是为什么不直接使用Parallel::ForkManager而不是强制Parallel::Prefork来模拟它呢?

使用
Devel::Trace
Devel::Trace::Fork
运行时,我发现
$game\u iterator->()
只被调用了一次。事实上,你的程序永远不会退出该方法中的第一个
while
循环。我还想指出,第一个示例中的两个
while
循环的顺序可以切换以获得相同的结果。顺便说一句,
while($fm->signal\u received ne'TERM'))
没有任何意义,因为您没有指示
$fm
捕获术语。Parallel::Prefork的pod没有声明它,但是它的构造函数包含
trap\u signals=>{TERM=>'TERM',},
节点之间不共享信息,它们只是从一组不同的数据开始工作。请注意Parallel::ForkManager示例代码。Parallel::Prefork本身声称它“很像Parallel::ForkManager,但支持优雅的关闭和运行时重新配置”。我使用的是ForkManager,但cpan审查让我相信,当我发送一个术语信号时,Prefork将是停止所有子进程的简单解决方案。我会自己处理,我想我会用ForkManager。哦,我明白了。然后继续使用Prefork