私の主な目標は、オブジェクトの大きなリストのためにいくつかの(外部的に時間がかかる)高価な作業を行うことです。そのために、もし私がそれをまっすぐにするなら、それは多くの時間がかかります。だから、私はパラレルモードに行き、いくつかの子プロセスをフォークする(4-8、みましょう)。メイン(親)プロセスでは、ワンプロセスバージョンの進捗状況に関する同じ統計情報をすべて出力したかったのです。複数の子プロセスをフォークして読み込む方法は?
しかし、私は4つの子プロセスをフォークしていくつかの作業を行うと、それらが生きていることがわかりますが、実際に何かをして親に情報を戻しているだけです。
これまで私が行ったコードは次のとおりです。時間のかかる部分は、その動作を非常にうまくシミュレートするランダムなスリープ状態に惑わされています。
#!/usr/bin/env perl
use strict;
use warnings;
use DateTime;
use DateTime::Format::HTTP;
use Time::HiRes;
my @to_be_processed = (1..300000);
my @queues;
my $nprocs = 4;
my $parent_from_child;
my @child_from_parent;
my @child_to_parent;
$SIG{CHLD} = 'IGNORE';
$|=1; # autoflush
my %stat = (
total => scalar(@to_be_processed),
processed => 0,
time_started => [Time::HiRes::gettimeofday],
);
# divide the list into queues for each subprocess
for (my $i = 0; $i < $stat{total}; $i++) {
my $queue = $i % $nprocs;
push @{$queues[$queue]}, $to_be_processed[$i];
}
# for progress simulation
srand (time^$$);
for (my $proc = 0; $proc < $nprocs; $proc++) {
# set up the pipes
pipe $parent_from_child, $child_to_parent[$proc] or die "pipe failed - $!";
# fork
defined(my $pid = fork) or die "fork failed - $!";
if ($pid) {
# parent
close $child_to_parent[$proc];
printf("[%u] parent says: child %u created with pid %u\n", $$, $proc, $pid);
}
else {
# child
close $parent_from_child;
open(STDOUT, ">&=" . fileno($child_to_parent[$proc])) or die "open failed - $!";
warn(sprintf("[%u] child alive with %u entries\n", $$, scalar(@{$queues[$proc]})));
foreach my $id (@{$queues[$proc]}) {
printf("START: %s\n", $id);
# simulation of progress
my $random_microseconds = int(rand(3000000))+200000;
warn(sprintf("[%u] child 'works' for %u microseconds", $$, $random_microseconds));
Time::HiRes::usleep($random_microseconds);
printf("DONE\n")
}
exit(0);
}
}
# parent: receive data from children and print overall statistics
while (<$parent_from_child>) {
chomp(my $line = $_);
if ($line =~ m/^START: (\S+)/) {
my ($id) = @_;
printf("%6u/%6u", $stat{processed}, $stat{total});
if ($stat{time_avg}) {
my $remaining = ($stat{total} - $stat{processed}) * $stat{time_avg};
my $eta = DateTime->from_epoch(epoch => time + $remaining);
$eta->set_time_zone('Europe/Berlin');
printf(" (ETA %s)", DateTime::Format::HTTP->format_isoz($eta));
}
printf("\r");
}
elsif ($line =~ /^DONE/) {
$stat{processed}++;
$stat{time_processed} = Time::HiRes::tv_interval($stat{time_started});
$stat{time_avg} = $stat{time_processed}/$stat{processed};
}
else {
printf("%s\n", $line);
}
}
通常、警告は削除する必要があります。 これを実行すると、1つの子だけが動作することがわかります。 私の質問は:なぜですか?私のミスはどこにあり、どのようにして仕事をしているのですか?
おかげ K.
素晴らしい!どうもありがとう!はい、私は同じハンドルで読むことができると思って、それはフラッシュなどで動作します。私はIO :: Selectとcan_read()ループを使用していましたが、これは魅力的です。 –
もう1つのコメント:私はsrandコールを子プロセスに移動するのを忘れていました。親の中でそれを初期化することは、もちろんすべての子供のために同じシーケンスにつながります。 –