Perl: один родитель, много детей - один читатель канала в родительском? - PullRequest
0 голосов
/ 09 октября 2018

Возможно ли в Perl установить канал таким образом, чтобы у родителя был только один канал READER, и многие дети записывали в него, когда они оживают / выходят?

Типичный код поваренной книги:

#!/usr/bin/perl -w
# pipe2 - use pipe and fork so child can send to parent

use IO::Handle;
pipe(READER, WRITER);
WRITER->autoflush(1);

if ($pid = fork) {
    close WRITER;
    chomp($line = <READER>);
    print "Parent Pid $$ just read this: `$line'\n";
   // do what you need
} else {
    die "cannot fork: $!" unless defined $pid;
    close READER;
    print WRITER "Child Pid $$ is sending this\n";
    close WRITER;  # this will happen anyway
    exit;
}

Давайте вместо этого предположим случай, когда мне нужно, чтобы мой родительский «ЧИТАТЕЛЬ» получал сообщения от нескольких дочерних элементов, возможно ли это сделать без сохранения списка каналов, по одному на каждый дочерний элемент?Я не могу закрыть WRITER в родительском элементе, потому что следующий дочерний элемент не получит действительный дескриптор для записи.Мне также нужно, чтобы родитель продолжал свою обычную работу и не блокировал какие-либо данные клиента из канала.

Псевдокод того, что мне нужно:

# parent code
pipe (READER, WRITER)
fork_random_number_of_children(READER,WRITER)

on_some_tick => {
    my $data = read_from(READER, non_blocking)
    if (data) print "Hey some child sent me: $data"
    else print "No data, going back life"
    do_other_things_before_next_tick()
}

child_job(R,W) { # lets assume this is called for each child fork
  close (R); # no problem, its a copy
  sleep (random duration)
  print W, "Message from child with pid $$"
  exit 0
}

1 Ответ

0 голосов
/ 09 октября 2018

Я не думаю, что необходимо close WRITER в родителе.Это может быть хорошей практикой, но, поскольку вы не можете повторно использовать один и тот же канал для новых дочерних процессов после его закрытия, это хороший повод не делать этого.Если вы оставите WRITER открытым до тех пор, пока не закончите запуск всех своих дочерних процессов, вы можете повторно использовать канал с несколькими дочерними процессами.Вот подтверждение концепции:

use IO::Handle;
use POSIX ':sys_wait_h';
pipe(READER,WRITER);
WRITER->autoflush(1);

sub child_process {
    my $stage = shift;
    close READER;  # also a best but optional practice
    srand($$);
    do {
        sleep 1 + 5*rand();
        print WRITER "Child Pid $$ ($stage) is sending this\n";
    } while (rand > 0.5);
    exit;
}

# initial set of children
for (my $i=0; $i<5; $i++) {
    if (fork() == 0) {
        child_process("LAUNCH");
    }
}

# parent
my ($rin,$rout) = ('');
vec($rin,fileno(READER),1) = 1;
while (1) {
     # non-blocking read on pipe
     my $read_avail = select($rout=$rin, undef, undef, 0.0);
     if ($read_avail < 0) {
         if (!$!{EINTR}) {
             warn "READ ERROR: $read_avail $!\n";
             last;
         }
     } elsif ($read_avail > 0) {
         chomp(my $line = <READER>);
         print "Read in Parent $$: '$line'\n";
     } else {
         print STDERR "No input ... do other stuff\n";
         # start some run-time child processes
         if (time-$^T > 5 && time-$^T < 10) {
             # launch a few more children in the middle of the program
             if (fork() == 0) {
                 child_process("RUN");
             }
         }
         sleep 1;
     }
     last if waitpid(-1,&WNOHANG) < 0;    # no more children are alive
}
close WRITER;  # now it is safe to do this ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...