Perl 循环内部分叉防止迭代器使用Parallel::Prefork递增
我有一些代码,我希望输出是1和6,但它输出1个无穷大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
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