내 주요 목표는 개체의 큰 목록에 대한 일부 (외부 시간이 소요 비싼) 작업을 수행하는 것입니다. 그것을 위해, 내가 그것을 곧장 앞으로하면, 그것은 많은 시간이 걸린다. 그래서 나는 병렬 모드로 가서 자식 프로세스들을 포크 화 (fork, fork, fork)하고 더 작은 세트의 객체에 대한 작업을 수행하기로 결정했다. 메인 (부모) 프로세스에서 원 프로세스 버전에 대해 수행했던 전반적인 통계 정보를 인쇄하고 싶었습니다.여러 하위 프로세스를 포크하고 읽는 방법은 무엇입니까?
그러나 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);
}
}
일반적으로 경고는 제거해야합니다. 실행하면 자식 하나만 작동한다는 것을 알 수 있습니다. 내 질문은 : 왜? 내 실수는 어디에서 발생하며 어떻게해야합니까?
감사 K.
좋아요! 많은 감사합니다! 예, 저는 같은 핸들을 읽을 수 있고 플러싱과 함께 작동한다고 생각했습니다. 나는 이제 IO :: Select와 can_read() 루프를 사용하여 그것을 수행했다. 이것은 매력처럼 작동한다. –
덧글 추가 : 저는 Srand Call을 하위 프로세스로 이동하는 것을 잊었습니다. 부모에서 그것을 초기화하는 것은 물론 모든 어린이들에게 동일한 순서로 이어진다. –