Как я могу предотвратить блокировку родителя при записи ребенку? - PullRequest
3 голосов
/ 09 июня 2009

Недавно у меня возникла проблема с использованием (pipe | -), когда я хотел установить связь между двумя процессами. По сути, дочерний процесс не может обработать STDIN так быстро, как он был заполнен родителем. Это заставило родителя ждать освобождения STDIN и замедлить его работу.

Насколько большим может быть STDIN и можно ли его изменить. Если да, каков наилучший размер практики?

Вот пример кода, чтобы показать, что я имею в виду:

if ($child_pid = open($child, "|-"))
{
    $child->autoflush(1);

    # PARENT process
    while (1)
    {

             # Read packet from socket save in $packet
             process_packet($packet);

             # forward packet to child
             print $child $packet;
     }
}
else
{
     die "Cannot fork: $!" unless defined $child_pid;
     # CHILD process
     my $line;  

     while($line = <STDIN>)
     {
         chomp $line;
         another_process_packet($line);
     }
}

В этом примере another_process_packet медленнее, чем process_packet. Причина, по которой я пишу такой код, заключается в том, что я хочу использовать одни и те же данные из сокета и получать их один раз.

Заранее спасибо.

Ответы [ 3 ]

6 голосов
/ 09 июня 2009

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

use AnyEvent;
use AnyEvent::Handle;

# make child, assume you write to it via $fh

my $done = AnyEvent->condvar;
my $h = AnyEvent::Handle->new( fh => $fh );

while( you do stuff ){
    my $data = ...;
    $h->push_write($data); # this will never block
}

$h->on_drain(sub { $done->send });
$done->wait; # now you block, waiting for all writes to actually complete

Редактировать: Раньше это было непроверенным, но я проверил, и это работает. (Я использовал perl -ne "sleep 1; print $_" в качестве медленного потомка.) Запись продолжается во время цикла while, если это возможно, но никогда не блокирует цикл. В конце вы фактически блокируете, пока все записи не будут завершены.

Мои тестовые сценарии на gist.github: http://gist.github.com/126488

Вы можете видеть, как дочерний элемент блокирует цикл блокировки, но как он не блокирует неблокирующий цикл. Очевидно, когда ты так говоришь;)

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

1 голос
/ 09 июня 2009

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

0 голосов
/ 16 июня 2009

Дескриптор процесса содержит функцию-член с именем «блокирование». Просто установите блокировку на 0, и родительский процесс не будет заблокирован.

if ($child_pid = open($child, "|-"))
{
    $child->blocking(0);    # Key to the solution.
    $child->autoflush(1);

    # PARENT process
    while (1)
    {

             # Read packet from socket save in $packet
             process_packet($packet);

             # forward packet to child
             print $child $packet;
     }
}
else
{
     die "Cannot fork: $!" unless defined $child_pid;
     # CHILD process
     my $line;  

     while($line = <STDIN>)
     {
         chomp $line;
         another_process_packet($line);
     }
}
...