以下代码运行2个孩子,他们将等待10秒并终止。父母坐在一个循环中,等待孩子们终止:
#!/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";
代码基于以下答案:Multiple fork() Concurrency
它工作正常,但父循环正在吃处理器时间。 top
命令给出了这个:
Here答案是:
作为额外奖励,循环将在
waitpid
时阻止 孩子们正在跑步,所以你在等待的时候不需要忙碌的循环。
但对我而言,它并没有阻止。怎么了?
答案 0 :(得分:6)
您正在传递WNOHANG
标记,这会使呼叫无阻塞。删除此标志,waitpid
将等待0%CPU,直到孩子退出。
如果采用这种方法,可以简化代码。在孩子完成之前无需循环,因为阻止waitpid
调用将为您做到这一点:
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;
}
}
或者,将sleep
调用更改为等待一秒钟。然后你的程序将每秒检查已完成的孩子,而不会增加CPU使用率。
答案 1 :(得分:0)
由于你的父线程在等待它的孩子时实际上没有做任何事情,我会将其简化为
#!/usr/bin/perl
use strict;
use warnings;
$| = 1; # autoflush
sub func{
my($i) = @_;
print "$i started\n";
sleep(10);
print "$i finished\n";
}
my $n = 2;
my @children_pids;
for my $i ( 0..($n-1) ) { # faster, and clearer than the C-style for loop
my $pid = fork;
die "Unable to fork" unless defined $pid; # check for errors
if ( $pid == 0) { # child
func($i);
exit(0); # may need to be POSIX::_exit()
} else { # parent
push @children_pids, $pid; # don't allow undef or 0 on the list
}
}
# while( @children_pids ){
# waitpid shift @children_pids, 0;
# }
waitpid $_, 0 for @children_pids;
print "parent finished\n";
如果你的perl是用IThreads编译的,你可以使用threads模块 (Windows上的fork仿真需要IThreads)
使用threads还可以更轻松地完成您最初尝试的操作,并在完成后加入线程。
use strict;
use warnings;
use threads (); # not using async
$| = 1; # autoflush
sub func{
my($i) = @_;
print "$i started\n";
sleep(rand(10)); # randomize the order of completion for this example
print "$i finished\n";
return $i; # <===
}
my $n = 10;
for my $i ( 0..($n-1) ){
my $thread = threads->create( \&func, $i ); # ask for scalar value
die "unable to create thread $i" unless defined $thread;
}
while( threads->list ){
# join the threads that are done
for my $thread ( threads->list(threads::joinable) ){
print 'thread-id: ', $thread->tid, ' returned: ', $thread->join, "\n";
}
# be nice to other threads and processes
threads->yield;
# allows the threads to "bunch up" for this example
# not necessary for real code.
sleep 2;
}
0 started
1 started
2 started
3 started
4 started
5 started
6 started
7 started
7 finished
8 started
9 started
2 finished
thread-id: 3 returned: 2
thread-id: 8 returned: 7
8 finished
5 finished
thread-id: 6 returned: 5
thread-id: 9 returned: 8
1 finished
thread-id: 2 returned: 1
3 finished
6 finished
9 finished
4 finished
thread-id: 4 returned: 3
thread-id: 5 returned: 4
thread-id: 7 returned: 6
thread-id: 10 returned: 9
0 finished
thread-id: 1 returned: 0