这是一个使用Parallel::ForkManager 生成单独进程的简单示例。
在每个进程中,STDOUT 和 STDERR 流被重定向,在演示中以两种方式重定向:STDOUT 到一个变量,然后可以根据需要传递(这里转储到一个文件中),以及 @ 987654331@ 直接写入文件。或者使用一个库,在单独的代码 sn-p 中有一个示例。
数字1..6 代表每个孩子将从中挑选并处理的数据批次。只有三个进程会立即启动,然后当一个进程完成时,另一个进程会在其位置启动。†(在这里它们几乎立即退出,“工作”是微不足道的。)
use warnings;
use strict;
use feature 'say';
use Carp qw(carp)
use Path::Tiny qw(path);
use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new(3);
foreach my $data (1..6) {
$pm->start and next; # start a child process
proc_in_child($data); # code that runs in the child process
$pm->finish; # exit it
}
$pm->wait_all_children; # reap all child processes
say "\nParent $$ done\n";
sub proc_in_child {
my ($data) = @_;
say "Process $$ with data $data"; # still shows on terminal
# Will dump all that was printed to streams to these files
my (outfile, $errfile) =
map { "proc_data-${data}_" . $_ . ".$$.out" } qw(stdout stderr);
# Redirect streams
# One way to do it, redirect to a variable (for STDOUT)...
open my $fh_stdout, ">", \my $so or carp "Can't open handle to variable: $!";
my $fh_STDOUT = select $fh_stdout;
# ...another way to do it, directly to a file (for any stream)
# (first 'dup' it so it can be restored if needed)
open my $SAVEERR, ">&STDERR" or carp "Can't dup STDERR: $!";
open *STDERR, ">", $errfile or carp "Can't redirect STDERR to $errfile: $!";
# Prints wind up in a variable (for STDOUT) and a file (for STDERR)
say "STDOUT: Child process with pid $$, processing data #$data";
warn "STDERR: Child process with pid $$, processing data #$data";
close $fh_stdout;
# If needed to restore (not in this example which exits right away)
select $fh_STDOUT;
open STDERR, '>&', $SAVEERR or carp "Can't reopen STDERR: $!";
# Dump all collected STDOUT to a file (or pass it around, it's a variable)
path( $outfile )->spew($so);
return 1
}
虽然STDOUT 被重定向到一个变量,STDERR 不能以这种方式重定向,它直接转到一个文件。见open。但是,也有一些方法可以在变量中捕获它。
然后您可以使用模块从子进程返回到父进程的能力,然后父进程可以处理这些变量。参见例如 this post 和 this post 和 this post。 (还有更多,这些只是我知道的。)或者实际上只是将它们转储到文件中,就像这里所做的那样。
另一种方法是使用可以运行代码和重定向输出的模块,例如Capture::Tiny
use Capture::Tiny qw(capture);
sub proc_in_child {
my ($data) = @_;
say "Process $$ with data $data"; # on terminal
# Run code and capture all output
my ($stdout, $stderr, @results) = capture {
say "STDOUT: Child process $$, processing data #$data";
warn "STDERR: Child process $$, processing data #$data";
# return results perhaps...
1 .. 4;
}
# Do as needed with variables with collected STDOUT and STDERR
# Return to parent, or dump to file:
my ($outfile, $errfile) =
map { "proc_data-${data}_" . $_ . ".$$.out" } qw(stdout stderr);
path($outfile) -> spew( $stdout );
path($errfile) -> spew( $stderr );
return 1
}
† 这样可以保持相同数量的进程运行。或者,可以将其设置为等待整个批次完成,然后再开始另一批次。一些操作细节见this post