2016-11-05 2 views
1

Так у меня есть сценарий PERL, который выходит там и wgets части потока (я не знаю, сколько штук есть авансом)Как остановить разветвление в этом коде

, но я не могу думать о хороший способ узнать, когда нужно остановить игру. прямо сейчас, если wget возвращается неудачно, мы создаем файл с именем «end», и как только основная программа увидит его, он остановит цикл. Есть ли лучший способ сделать это?

Очевидно, было бы легко, если бы это было сделано последовательно, а не одновременно, но я пытался сделать его самым быстрым.

my $link = $ARGV[0]; 
my ($url) = $link=~ m/(.+-)\d+.ts/i; 

my $num = 0; 

#while the file END doesn't exist 
my @pids; 
while (! -e "END") { 
     #create the URL, increment by 1 
     my $video=$url.++$num.".ts"; 
     die "could not fork" unless defined (my $pid = fork()); 

     #child process goes until wget returns invalid, create END 
     if (not $pid) { 
       system ("wget -T 5 -t 5 $video"); 
       `touch END` if $? != 0; 
       exit; 
     } 
     push @pids, $pid; 
} 

#parent process still running, waiting for the same END file. 
for my $pid (@pids) { waitpid $pid,0; } 

print "pids finished\n"; 

sleep 1; 
`rm END`; 

ответ

3

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

Это еще более важно, когда вы выходите в сеть, так как вы можете раздражать сервер (и все также перестанет быстро развиваться). Возможно, до нескольких десятков процессов одновременно?

Тогда один из вариантов - ограничить количество параллельных загрузок, используя Parallel::ForkManager. Он имеет способ return data to parent, поэтому ребенок может сообщить об ошибке. Затем его метод run_on_finish может проверять каждую партию для такого флага (отказа) и устанавливать переменную, которая управляет форсированием.

use warnings; 
use strict; 
use Parallel::ForkManager;  

my $pm = new Parallel::ForkManager(2); # only 2 for a managable demo 
my $stop_forking; 

# The sub gets 6 parameters, but only first (pid) is always defined 
# The last one is what a child process may have passed 
$pm->run_on_finish( 
    sub { $stop_forking = 1 if defined $_[-1] } 
); 

for my $i (0..9) 
{ 
    last if $stop_forking; 

    $pm->start and next; # forks 
    my $ret = run_job($i); # child process 

    # Pass data to parent under a condition 
    if ($ret eq 'FAIL') { $pm->finish(0, \$ret) } # child exits 
    else    { $pm->finish } 
} 
$pm->wait_all_children; 

sub run_job { 
    my ($i) = $_[0]; 
    sleep 2; 
    print "Child: job $i exiting\n"; 
    return ($i == 3 ? 'FAIL' : 1); 
} 

Это останавливает разветвление после серии работ, в течение которого $i == 3. Добавьте отпечатки для диагностики.

Комментарии. «Обратный вызов» run_on_finish при его запуске запускается только после завершения целой серии. & lowast;анонимный подканал в нем всегда принимает 6 аргументов, но всегда определяется только первый, дочерний pid. У последнего есть данные, которые могут быть переданы дочерним, и когда это произойдет, мы устанавливаем флаг. Ребенок может возвращать данные, передавая скалярную ссылку на метод finish. Чтобы указать только условие, мы можем просто передать что-либо. Я использую \$ret в качестве примера передачи фактических данных.

Подробнее см. Документацию, но это делает то, что вам нужно.


Если вы хотите, чтобы раскошелиться, как и вы, я первый положил в небольшой sleep там, так что вы не бомбардируют сервер слишком большим числом запросов. Ваши дети могут разговаривать с родителями, используя socketpair. Неисправный ребенок может писать, а остальные могут просто закрыть свой сокет. Родитель продолжает проверять, например, с can_read от IO::Select. Пример: perlipc. Поскольку вам нужны только дети, чтобы написать родителям, достаточно было и pipe.

Вы также можете сделать это с помощью сигнала. Ребенок, который не отправляет (скажем) SIGUSR1 родительскому объекту, который родительский ловушки и устанавливает глобальную переменную, которая контролирует дальнейшие вилки. Это проще, так как родительский ловушки только один сигнал и не заботятся о том, откуда он. См. perlipc и sigtrap прагма.

Вы также можете использовать файл, как и вы, который, вероятно, прост, так как здесь вас не волнуют проблемы с гонкой (независимо от того, записывают ли дети перекрытие), но только о том, что появляется пустой файл.

Однако во всех этих случаях вы также хотите ограничить количество параллельных процессов.

Наконец, существует ряд модулей, которые могут помочь с этим, например IPC::Run.


& lowast; Чтобы запустить обратный вызов, так как каждый ребенок выходит из системы reap_finished_children. См. this post.

+0

спасибо за информацию, я сделаю некоторые исследования. Я не знаю, сколько файлов есть для определенного потока, поэтому прямо сейчас я иду, пока wget не сработает. но вы правы, я должен учитывать сервер и не бомбардировать его. последний, который я запустил, составил более 357 фрагментов видео. он действительно загружал очень быстро, хотя. – genx1mx6

+0

@ genx1mx6 Надеюсь, это поможет, даже если в нем в основном перечислены подходы. Я планирую добавить код с 'P :: FM' (сейчас уже поздно). Родитель просто запускается через цикл, чтобы те 357 были созданы сразу, и вы, вероятно, попали на сервер со всем этим сразу с места в карьер. Там, должно быть, было еще больше, так как для родителя нет времени, чтобы вернуться в следующую итерацию, показывая еще одну, поэтому она просто продолжает вращать их. Даже в случае сбоя, вероятно, существует целая группа, пытающаяся загрузить другие несуществующие файлы. Btw, файл, который вы используете, в порядке, но вам нужно ограничить число. – zdim

+0

@ genx1mx6 Я добавил рабочий пример с 'Parallel :: ForkManager'. Я могу добавить код для других методов, но дайте мне знать, как это происходит. – zdim

Смежные вопросы