The following code runs 2 children, who will wait for 10 seconds and terminate. The parent is sitting in a loop, waiting for the children to terminate:
#!/usr/bin/perl
use strict;
use warnings;
use POSIX ":sys_wait_h";
sub func
# {{{
{
my $i = shift;
print "$i started\n";
$| = 1;
sleep(10);
print "$i finished\n";
}
# }}}
my $n = 2;
my @children_pids;
for (my $i = 0; $i < $n; $i++) {
if ((my $pid = fork()) == 0) {
func($i);
exit(0);
} else {
$children_pids[$i] = $pid;
}
}
my $stillWaiting;
do {
$stillWaiting = 0;
for (my $i = 0; $i < $n; ++$i) {
if ($children_pids[$i] > 0)
{
if (waitpid($children_pids[$i], WNOHANG) != 0) {
# Child is done
print "child done\n";
$children_pids[$i] = 0;
} else {
# Still waiting on this child
#print "waiting\n";
$stillWaiting = 1;
}
}
#Give up timeslice and prevent hard loop: this may not work on all flavors of Unix
sleep(0);
}
} while ($stillWaiting);
print "parent finished\n";
The code is based upon this answer: Multiple fork() Concurrency
It works correctly, but the parent loop is eating processor time. top
command gives this:
Here the answer says:
As an additional bonus, the loop will block on
waitpid
while children are running, so you don't need a busy loop while you wait.
But for me it doesn't block. What's wrong?
You're passing the WNOHANG
flag, which makes the call non-blocking. Remove this flag and waitpid
will wait at 0% CPU until the child quits.
If you take this approach, you could simplify the code. There's no need to loop until a child is finished, because the blocking waitpid
call will do that for you:
for (my $i = 0; $i < $n; ++$i) {
if ($children_pids[$i] > 0) {
waitpid($children_pids[$i], 0);
print "child done\n";
$children_pids[$i] = 0;
}
}
Alternatively, change the sleep
call to wait for one second. Then your program will check for finished children every second, without pushing up CPU usage.