in reply to Re^2: Forking problem (manager)
in thread Forking problem UPDATED

after each child finishes there should be another one created

That's exactly what Parallel::ForkManager does.

Say you have 5 tasks, and you set a max of 3 children. ForkManager will create 3 children and assign them tasks 0, 1 and 2. When one of these end, another child is created for task 3. When one of child ends, another child is created for task 4. Having no more tasks to assign, it waits for the 3 children to end.

The implementation is very simple:

use Parallel::ForkManager qw( ) use constant MAX_PROCESSES => 3; my $pm = Parallel::ForkManager->new(MAX_PROCESSES); foreach $data (@array) { # Forks and returns the pid for the child: my $pid = $pm->start and next; ... do some work with $data in the child process ... $pm->finish; # Terminates the child process }

Replies are listed 'Best First'.
Re^4: Forking problem (manager)
by avi_learns_perl (Novice) on Mar 07, 2007 at 09:26 UTC
    yes...this is when each time the fork does the same job with different $ data....but i have 3 different jobs using $data from array such that 3 run simultaneously... example for $data1 I do print $data1 for $data2 i do system "/home/working/perl1.pl ".$data2; for $data3 I do system "/home/perl2.pl ".$data3 all three have to run simultaneously and when any one ends $data4 has to be sent as input to that program...example assuming $data2 finishes the fork should then execute the system"/home/working/perl1.pl ".$data4;...this goes on as you say till all data in @array is exhausted

      ah, I see! Very "different". Yeah, Parallel::ForkManager won't help you. I thought I had a solution involving $pm->run_on_finish, but I don't.

      Update: I'm working on a small patch to ForkManager that will help. Soon...

      Then use the method I outlined. It is quite simple as far as IPC goes. You could use one of the IPC modules but I think they mostly subtitute one kind of complexity for another in this case rather than actually making things a lot simpler.

      It boils down to the basic, typical use of fork and pipe. Parent creates pipe then forks N times. Parent closes read-from side of pipe and each child closes write-to side of pipe. Then parent can write small packets (each packet needs to be send via a separate syswrite, which you can accomplish by just turning on buffer auto flushing for the pipe filehandle and being sure to write the packet with a single print or similar) and each child can read from the pipe and atomically get the next packet and each packet will only go to one child.

      I doubt it would be 2 dozen lines of code.

      Parent should likely wait for each child after it has finished writing to and closeing the pipe (so it might want to save up the list of PIDs from forking, but it doesn't actually have to).

      Each child boils down to a very simple close WRITE_TO; while( <READ_FROM> ) { doStuff( $array[$_] ) }.

      - tye        

      I have a solution, but it requires making a couple of changes to Parallel::ForkManager.

      Solution:

      use strict; use warnings; use Parallel::ForkManager qw( ); my @data = ( ... ); my @tasks = ( sub { printf("%s\n", $_[0]); }, sub { exec('/home/working/perl1.pl', $_[0]); }, sub { exec('/home/perl2.pl', $_[0]); }, ); { my $task_id; my @idle = (0..$#tasks); my %alloc; my $pm = Parallel::ForkManager->new(scalar(@tasks)); $pm->run_pre_fork(sub { $task_id = shift(@idle); }); $pm->run_on_start(sub { my ($pid) = @_; $alloc{$pid} = $task_id; }); $pm->run_on_finish(sub { my ($pid) = @_; push(@idle, delete($alloc{$pid})); }); foreach my $data (@data) { my $pid = $pm->start and next; $tasks[$task_id]->($data); $pm->finish; } }

      Changes to site/lib/Parallel/ForkManager.pm:

      • Change

        $s->wait_children; if ($s->{max_proc}) { my $pid=fork();

        in start to

        $s->wait_children; $s->pre_fork; if ($s->{max_proc}) { my $pid=fork();
      • Add

        sub run_pre_fork { my ($s,$code)=@_; $s->{pre_fork}=$code; } sub pre_fork { my ($s)=@_; $s->{pre_fork}->() if ref($s->{pre_fork}) eq 'CODE'; }

        auto.pl!!...execute this file alone!
        system "ls -1 /home/input/ > /home/list";
        my @test = ();
        @array=qw{1.pl 2.pl};
        foreach $i(@array)
        {
        my $pid = fork();
        if($pid==0)
        {
        #child
        system "perl ".$i
        ; exit(0);
        }
        elsif($pid)
        {
        #parent
        push(@test,$pid);
        }
        }
        foreach (@test)
        {
        waitpid($_,0);
        }

        1.pl!!!!!!!!
        my @array=();
        my %hash=();
        open (LIST, "</home/list");
        @array= <LIST>;
        close LIST;
        foreach (@array){
        chop($_);
        }
        $len=@array;
        $rc=0;
        $i=0;
        $y=0;
        begin: while($i<$len)
        {
        while($rc<3)
        {
        my $rcpid= fork();
        if($rcpid==0)
        {
        system "/home/x.pl $array$i";
        print "child".$i."\n";
        exit(0);
        }
        elsif($rcpid)
        {
        print "Parent ".$rcpid."\n";
        push(@kick,$rcpid);
        $rc++;
        $a = $array$i;
        $hash{ $i } = $a;
        $i=$i+2;
        }
        }
        $x=$i-6;
        if($rc==3)
        {
        while($x < $i)
        {
        $temp=$array$x.".out";
        if (-e "/home/$temp")
        {
        delete $hash{$x};
        $rc--;
        print "inside begin\n";
        goto begin;
        break;
        }
        else
        {
        $x=$x+2;
        }
        }
        }
        foreach (@kick)
        {
        waitpid($_,0);
        }
        sorry couldnt figure out how to give the spaces at the beginning of the line...and not a lot of time too...my apologies to fellow monks...this can be extended to any numbers...i have given example of 1.pl...you can do this till n i guess and also you can change the number of forks per file....hope this aint bad perl hacking...:-s ...cheers