http://www.perlmonks.org?node_id=1073412


in reply to Please Explain the Parallel::ForkManager Idiom my $pid = $pm->start and next;

It can be confusing, sure.   Think of it this way:   when the parent process start()s a child process, there are, from that moment forward, two processes executing more-or-less parallel with one another.   (I say “more-or-less” because the relative timing of the two can’t be exactly predicted ...)   Nevertheless, both are executing the same Perl code, at the same point, but with exactly one very important difference:

The “fork in the road” (heh...) therefore happens with and next.   This is shorthand, exploiting Perl’s use of “short-circuit” expression evaluation.   The parent immediately continues with the foreach loop (or ends it); the child proceeds.   (Here’s the short-circuit:   since 0 and anything is known to be false, the right side of the and clause (that is to say, the next statement) is never evaluated in the child.)

Ordinarily, the console-output of both processes will now be intermingled on the screen, in no particular (exactly predictable) order, and yes, you will see that the child does sleep() properly, as any process would do.   If you still don’t observe that, please post a snippet of your code (remember to use <code> tags ...) so that we can point out the error of your ways.   It is a bit tricky ...

Replies are listed 'Best First'.
Re^2: Please Explain the Parallel::ForkManager Idiom my $pid = $pm->start and next;
by Jim (Curate) on Feb 04, 2014 at 20:20 UTC

    It's not terribly important to this discussion that I mention this, but the and next bit is not part of what's confusing me. I understand this part of the idiom.

    However, instead of doing this…

    CHILD: foreach my $child ( 0 .. $#names ) { my $pid = $pm->start($names[$child]) and next; # Child process... }

    …I would typically prefer to do this…

    CHILD: for my $child ( 0 .. $#names ) { my $pid = $pm->start($names[$child]); next CHILD if $pid != 0; # Parent process # Child process... }

    I think this is more in line with PBP, but I could be mistaken.

    Jim

      Why? For the most part, I don't care and don't want to know what the pids are. P::FM does the bookkeeping for me. I can do something like:
      for my $name (qw(foo bar baz)) { $pm->start($name) and next; ... }
      And then I can think of my child processes as having names 'foo', 'bar', and 'baz' and can forget about pids. The names can be used in the run_on_finish() callback if I want to.
        Why?

        Exactly! That's the code in the demo script in the Parallel::ForkManager documentation. It's the example I was running and studying earlier. The PIDs and exit codes are included in the code for didactic reasons.

        You'll appreciate my similar demo script based on the same kids-in-line-at-the-pool theme.

        use strict; use warnings; use Parallel::ForkManager; $| = 1; my $MAXIMUM_KIDS_IN_POOL = 3; my $MAXIMUM_TIME_IN_POOL = 30; # Seconds my $lifeguard = Parallel::ForkManager->new($MAXIMUM_KIDS_IN_POOL); my @kids = qw( Ann Bob Cal Dan Eve Fay Gus Hal Ike Joe Kim Lee Meg ); $lifeguard->run_on_start( sub { my ($pid, $kid) = @_; print "\nThe lifeguard waves $kid into the pool\n"; } ); $lifeguard->run_on_finish( sub { my ($pid, $exit_code, $kid) = @_; print "\n$kid climbs out of the pool\n"; } ); $lifeguard->run_on_wait( sub { print "."; }, 0.3 ); my $number_of_kids = @kids; print "\nThere are $number_of_kids kids in line at the pool\n\n@kids\n +"; KID_IN_LINE: for my $kid (@kids) { my $pid = $lifeguard->start($kid); next KID_IN_LINE if $pid != 0; srand(); my $time = int(rand($MAXIMUM_TIME_IN_POOL)) + 1; print "\n$kid jumps into the pool\n"; sleep $time; print "\nThe lifeguard orders $kid out of the pool after $time sec +onds\n"; sleep 0.5; $lifeguard->finish(); } print "\nAll the kids have been in the pool\n"; $lifeguard->wait_all_children(); print "\nAll the kids are out of the pool\n"; exit 0;

        (As I post this script here, I notice there are vestiges of the original script still in the callback subs; namely, $pid and $exit_code. I could remove them.)

        Jim

        UPDATE:  And if I'm truly serious about not caring about the PIDs, I could do this instead:

        next KID_IN_LINE if $lifeguard->start($kid) != 0;

        ANOTHER UPDATE:  More weirdness. This works fine…

        $lifeguard->run_on_start( sub { my $kid = pop; print "\nThe lifeguard waves $kid into the pool\n"; } );

        …but this blows up with Use of uninitialized value $kid in concatenation (.) or string at Pool.pl line 27

        $lifeguard->run_on_finish( sub { my $kid = pop; print "\n$kid climbs out of the pool\n"; } );

        No variation of pop()-ing @_ in run_on_finish() works.

        YET ANOTHER UPDATE:  OK, this is documented in…ahem…the documentation.