Понимание того, как работает Perl fork - PullRequest
0 голосов
/ 15 января 2019

Каким будет правильный путь для разветвления процессов, когда каждый из них запускает отдельную подпрограмму sub1,sub2,...,subN. После прочтения большого количества предыдущей темы и материала я чувствую, что понимаю логику, но немного запутался в том, как писать максимально чистым способом (для меня важна читаемость).

Рассмотрим 4 подпункта. Каждый из них получает разные аргументы. Похоже, что наиболее эффективным способом было бы создать 7 вилок, каждый из которых будет работать по своему сабвуферу. Код будет выглядеть примерно так:

my $forks = 0;
foreach my $i (1..4) {
    if ($i == 1) {
        my $pid = fork();
        if ($pid == 0) {
            $forks++;
            run1();
            exit;
        }
    } elsif ($i == 2) {
        my $pid = fork();
        if ($pid == 0) {
            $forks++;
            run1();
            exit;
        }
    } elsif ($i == 3) {
        my $pid = fork();
        if ($pid == 0) {
            $forks++;
            run1();
            exit;
        }
    } elsif ($i == 4) {
        my $pid = fork();
        if ($pid == 0) {
            $forks++;
            run1();
            exit;
        }
    }

}

for (1 .. $forks) {
    my $pid = wait();
    print "Parent saw $pid exiting\n";
}
print "done\n";

Некоторые баллы:

  • Это будет работать, только если все вилки были успешны. Но я хотел бы запустить подпрограммы, даже если разветвление не удалось (даже если оно не будет параллельным. В этом случае, я думаю, нам нужно вытащить подпрограммы из if и выйти, только если $ pid не был 0. что-то вроде:

    my $pid = fork();
    run1();
    $forks++ if ($pid == 0);
    exit if ($pid == 0);
    

    Но это все еще не правильно.

  • Использование exit является правильным способом уничтожения дочернего процесса? если процессы были убиты с exit, я все еще должен использовать wait? Это предотвратит зомби?

  • Возможно, самый интересный вопрос: что я буду делать, если у нас будет 15 вызовов функций? Я хотел бы как-то создать 15 вилок, но я не могу создать 15 операторов if-else - код не будет читаемым таким образом. Сначала я подумал, что можно вставить эти вызовы функций в массив (каким-то образом) и зациклить этот массив. Но после некоторого исследования я не нашел способа, которым это возможно.

  • Если возможно, я предпочитаю не использовать никаких дополнительных модулей, таких как Parallel::ForkManager.

Существует ли простой и понятный способ ее решения?

1 Ответ

0 голосов
/ 15 января 2019

Здесь есть несколько вопросов, которые нужно прояснить.

Базовый пример

use warnings;
use strict;
use feature 'say';

my @coderefs;    
for my $i (1..4) { 
    push @coderefs, sub { 
        my @args = @_; 
        say "Sub #$i with args: @args";
    };
}

my @procs;
for my $i (0..$#coderefs) {
    my $pid = fork  // do {
        warn "Can't fork: $!";
        # retry, or record which subs failed so to run later
        next;
    };  
    if ($pid==0) { 
        $coderefs[$i]->("In $$: $i");
        exit;
    }   
    push @procs, $pid;
    #sleep 1;
}    
say "Started: @procs";

for (@procs) {
    my $goner = wait;
    say "$goner exited with $?";
}

Мы генерируем анонимные подпрограммы и сохраняем эти ссылки кода в массиве, затем проходим через этот массив и запускаем столько процессов, в каждом из которых выполняется подпрограмма. После этого родитель wait s на всех этих, но обычно вы будете использовать waitpid; см. документы, перечисленные ниже.

Дочерний процесс всегда exit с, иначе у вас будет несколько процессов, выполняющих весь остальной код в программе. Как только дочерний процесс завершает работу, ядро ​​уведомляет родительский процесс, и родитель может «забрать» это уведомление («пожинать» статус завершения дочернего процесса) через wait / waitpid или использовать обработчик сигнала, чтобы игнорировать это.

Если родитель никогда не делает этого после выхода дочернего элемента и выходит из него позже, то ОС остается застрявшей с этой информацией о (вышедшем) дочернем процессе в таблице процессов; это зомби. Так что вам нужно wait, чтобы ОС работала с дочерним процессом (и вы проверите, как он прошел). Или используйте обработчик сигнала, чтобы указать, что вы не заботитесь о выходе ребенка. Современные системы пожинают потенциальных зомби, но не всегда, и вы не можете на это полагаться; убери за собой.

Обратите внимание, вам нужно будет прочитать perlipc , fork , wait и waitpid , perlvar ... и многие другие страницы, с которыми вы столкнетесь, работая над всем этим. Это займет немного игры и проб и ошибок. После того, как вы разберетесь со всем этим, вы можете начать использовать модули, по крайней мере, для некоторых типов задач.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...