Как запустить параллельно две команды из родительской? - PullRequest
2 голосов
/ 06 января 2012

У меня есть два кода

1

use File::Temp qw(tempfile);
$tmp = new File::Temp( UNLINK => 0 );
system("tv_grab_au | tv_sort >> $file");
system("cp $file $HOME/.xmltv/listings.xml");

unlink($file);

2

while (-e $file) {
sleep 2;
system("tvtime-command DISPLAY_MESSAGE \'Updating TV Guide. Please wait this might take a several minutes...\'");
}

Я хотел бы объединить эти 2 кода для запуска tv_grab_au xmltv grabber (обновить TV Guide) и одновременно отправить команду в tvtime для отображения сообщения 'Updating TV Guide. Пожалуйста, подождите, это может занять несколько минут ... ', каждые две секунды, пока не появится $ file.

Я пробую это:

use strict;
use warnings;
use File::Temp qw(tempfile);
my $file = new File::Temp( UNLINK => 0 );
use POSIX qw(:sys_wait_h);
$|++;

defined(my $pid = fork) or die "Couldn't fork: $!";

if (!$pid) {    
    system("tv_grab_huro | tv_sort >> $file");
    unlink($file);
}
else { 
    while (! waitpid($pid, WNOHANG)) {
        system("tvtime-command DISPLAY_MESSAGE \'Updating TV Guide. Please wait this might take a several minutes...\'");
        sleep 2;
        }
}

Спасибо.

Ответы [ 2 ]

4 голосов
/ 06 января 2012

Встроенная функция fork создает копию вашей текущей программы в новом фоновом процессе.Исходный процесс и «дочерний» процесс будут выполняться одновременно.Таким образом, вы можете сделать что-то вроде:

use File::Temp qw(tempfile);
my $file = new File::Temp( UNLINK => 0 );

my $new_pid = fork();
die "fork failed $!" unless defined $new_pid;   # this is uncommon

# Task 1 - in the background
if ($new_pid == 0) {
    system("tv_grab_au | tv_sort >> $file");
    system("cp $file $HOME/.xmltv/listings.xml");    
    unlink($file);
    exit;            # don't forget this part!
}

# Task 2 - in the foreground
while (-e $file) {
    print "...";
    sleep 2;
}

Использование $file в качестве индикатора того, когда первая задача выполнена, имеет некоторые недостатки.Что если в дочернем коде есть какая-то ошибка во время выполнения?Что делать, если дочерний процесс прерывается?Дочерний процесс может завершиться до того, как ему удастся удалить $file.Тогда ваш цикл while в родительском процессе никогда не закончится.

Встроенная команда waitpid может проверить, работает ли дочерний процесс, и может обработать случай, когда дочерний процессзавершается ненормально.

# Task 2 
use POSIX ':sys_wait_h';
while (! waitpid $new_pid, &WNOHANG) {   # WNOHANG => non-blocking wait
    print "...";
    sleep 2;
}
0 голосов
/ 06 января 2012

Используйте fork (). Я добавил дополнительные вызовы sleep (), чтобы вы могли видеть, что процессы работают и работают. На практике обновление crontab, вероятно, будет выполняться достаточно быстро, чтобы цикл монитора вообще не запускался или выполнялся только один раз. Я использовал «разве что (...)», потому что мне кажется, что код стал понятнее; следует помнить, что fork () возвращает pid родительскому элементу и ноль дочернему. Процесс, который не видит pid, поэтому является подпроцессом. (Как уже указывалось, в случае сбоя разветвления вилка вернет undef, и код будет выполняться в исходном процессе. В нашем случае это будет просто означать, что мониторинг запускается после завершения записи, поэтому единственным что мы теряем, так это мониторинг.)

my $file = "/tmp/.$$.crontab.txt";
my $crontab = <<EOS;
# Crontab lines here. Inserted at @{[scalar localtime()]}
EOS

my ($writer_pid, $monitor_pid);

$|++;

# Open file BEFORE launching processes. The monitor depends on the file's
# presence or absence, so if we opened it in the writer process, there'd be a
# chance the monitor process would check before we created it, and exit without
# monitoring.
die "Cannot open temp file\n" unless open(WRITE, ">" . $file);

# Crontab file handle gets passed to the forked process, so we can just use it.
# Altered so we can see the process do its thing.
unless ($writer_pid = fork()) {
        print WRITE $crontab."\n";
        close WRITE;
        print("crontab -l |grep -v backup >> $file");
        sleep 20;
        print("crontab $file");
        sleep 10;
        unlink($file);
        print "done!\n";
        exit;
}

# Either file will exist, or the previous process will
# have completed. If it exists, we monitor. If not,
# we exit immediately.
unless ($monitor_pid = fork()) {
    # Child: monitor the writer.
    my $waitcount = 1;
    while ( -e $file ) {
       sleep 2;
       print  "($waitcount) installing crontab...";
       $waitcount++;
    }
    print "installed\n";
    exit;
}

waitpid($monitor_pid, 0);
waitpid($writer_pid,0);
print "both processes done\n";
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...