How to make waitpid block a loop

The following code starts 2 children who will wait 10 seconds and complete. A parent sits in a cycle, waiting for the children to finish work:

#!/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"; 

Code based on this answer: Multiple fork () Concurrency

It works correctly, but the parent cycle consumes CPU time. The top command gives the following:

enter image description here

Here the answer is:

As an added bonus, the cycle will block on waitpid while the children are working, so you do not need a busy cycle while you wait.

But for me it is not blocked. What's wrong?

+4
source share
2 answers

You pass the WNOHANG flag, which makes the call non-blocking. Remove this flag and waitpid will wait for 0% of the CPU until the child leaves.

If you take this approach, you can simplify the code. There is no need to quote until the child is completed, because the call to waitpid will do this 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 one second. Then your program will check the finished children every second, without increasing the processor load.

+6
source

Since your parent thread is not actually doing anything, expecting it, I would simplify it

 #!/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"; 

If your perl is compiled using IThreads, you can use the threads module.
(IThreads required for fork emulation on Windows)

Using threads also makes it much easier to complete your attempts, attaching threads as they complete.

 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 
0
source

Source: https://habr.com/ru/post/1485003/


All Articles