2012-06-06 3 views
10

У меня есть скрипт, который порождает набор детей. Родитель должен дождаться завершения каждого из них.Воспроизведение дочерних процессов с Perl

Мой сценарий выполняет аналогично следующий сценарий PERL:

#! /usr/bin/perl 
use strict; 
use warnings; 

print "I am the only process.\n"; 

my @children_pids; 

for my $count (1..10){ 
     my $child_pid = fork(); 
     if ($child_pid) { # If I have a child PID, then I must be the parent 
       push @children_pids, $child_pid; 
     } 
     else { # I am the child 
       my $wait_time = int(rand(30)); 
       sleep $wait_time; 
       my $localtime = localtime; 
       print "Child: Some child exited at $localtime\n"; 
       exit 0; # Exit the child 
     } 
} 

foreach my $child (@children_pids) { 
     print "Parent: Waiting on $child\n"; 
     waitpid($child, 0); 
     my $localtime = localtime; 
     print "Parent: Child $child was reaped - $localtime.\n"; 
} 

print "All done.\n"; 

Подобного код я Приведенный выше, каждый ребенок может занять разное время, чтобы закончить.

Проблема заключается в том, что я пытаюсь пожинать детей, перебирая дочерние PID, в этом последнем блоке foreach родитель ждет детей в том порядке, в котором они созданы.

Очевидно, что дети не заканчивают в порядке, в котором они рождаются, и поэтому я остаюсь с кучей процессов зомби для детей, которые заканчиваются рано.

В моем фактическом коде эти дети могут заканчивать дни друг перед другом, а число процессов зомби, плавающих вокруг, может вырасти в сотнях.

Есть ли лучший способ получить набор детей?

ответ

12

Если родительский процесс не должен знать о состоянии завершения своих детей, то вы можете просто установить

$SIG{CHLD} = 'IGNORE'; 

, которая будет автоматически пожинать все дети, как они полные.

Если вы сделать потребность быть в курсе детей завершения, то обработчик сигнала должен быть установлен, чтобы пожинать все возможные процессы

use POSIX(); 

$SIG{CHLD} = sub { 
    while() { 
    my $child = waitpid -1, POSIX::WNOHANG; 
    last if $child <= 0; 
    my $localtime = localtime; 
    print "Parent: Child $child was reaped - $localtime.\n"; 
    } 
}; 
+1

Основная причина быть в курсе: проверить, были ли они успешными. – ikegami

+0

Эй, Бородин, проверьте [Синтаксис :: Feature :: Loop] (http://search.cpan.org/perldoc?Syntax::Feature::Loop) – ikegami

+0

@ikegami: да, я иногда использую это. Я в основном переключаюсь между использованием while (1) ',' while() 'и' {... redo; } '. Ни один из них не является действительно удовлетворительным. – Borodin

6

использовать «-1» для pid или использовать функцию wait(), чтобы вы ожидали какого-либо дочернего процесса. Возвращается pid, поэтому вы можете проверить его в своем списке, если это необходимо. Если это неприемлемо, то периодически ожидайте ожидания для каждого pid в вашем списке с помощью POSIX :: WNOHANG() в качестве второго аргумента.

5

Borodin's answer прекрасно подходит для асинхронной жатвы детей, как они прекратить.

Если, как ваш вопрос и код предложить мне, вы ищете синхронное (блокирование) пожинает все выдающиеся ребенок в том порядке, в котором они оканчиваются, родитель может просто сделать это:

use feature qw(say); 

... 

# Block until all children are finished 
while (1) { 
    my $child = waitpid(-1, 0); 
    last if $child == -1;  # No more outstanding children 

    say "Parent: Child $child was reaped - ", scalar localtime, "."; 
} 

say "All done." 
1

никогда не используйте цикл, как это ждать детей:

while (1) { 
    my $child = waitpid(-1, POSIX::WNOHANG); 
    last if $child == -1; 
    print "Parent: Child $child was reaped\n"; 
} 

родительский процесс будет потреблять 100% CPU во время ожидания дочерних процессов, чтобы умереть - е особенно когда они могут работать в течение длительного времени. По крайней мере, добавьте сон (плохая идея - когда они умирают быстро, родитель ждет).

Всегда используйте блокирующее ожидание + рассчитывать на TERM/INT/PPID для любезности !:

my $loop = 1; 
$SIG{CHLD} = 'DEFAULT'; # turn off auto reaper 
$SIG{INT} = $SIG{TERM} = sub {$loop = 0; kill -15 => @children_pids}; 
while ($loop && getppid() != 1) { 
    my $child = waitpid(-1, 0); 
    last if $child == -1; 
    print "Parent: Child $child was reaped\n"; 
} 

Этой блокировка ждать его, конечно, не представляется возможным, когда родительский процесс также должен делать другие вещи - как getppid() вызов ;-).Для этого вы можете использовать socketpair() и поместить его в select(), который выполняет блокирующий вызов. Даже проверка цикла может выиграть от этого.

+0

Он не должен зацикливаться навсегда, вот что делает строка «last if». Если ни один ребенок не умер, он выходит из цикла. –

+0

Речь идет не о бесконечном цикле, а о циклах 100% CPU, используемых во время цикла. Если для завершения 200 процессов требуется 10 минут, это 1 процесс, который тратит 100% циклов процессора в течение 10 минут, а если вы блокируете, это в основном нет. – CowboyTim

Смежные вопросы