Как узнать, завершился ли процесс открытия канала? - PullRequest
5 голосов
/ 17 июня 2011

Предполагается, что дескриптор создан со следующим кодом:

use IO::File;

my $fh = IO::File->new;

my $pid = $fh->open('some_long_running_proc |') or die $!;

$fh->autoflush(1);
$fh->blocking(0);

, а затем читать с циклом, как это:

while (some_condition_here) {
    my @lines = $fh->getlines;
    ...
    sleep 1;
}

Что я могу указать как some_condition_here, который вернет false, если процесс на другом конце канала завершился?

Тестирование на $fh->eof не будет работать, поскольку процесс все еще может выполняться без печати каких-либо новых строк. Тестирование на $fh->opened, похоже, ничего полезного не дает.

В настоящее время я использую $pid =! waitpid($pid, WNOHANG), который, кажется, работает в POSIX-совместимых средах. Это лучший способ? Как насчет Windows?

Ответы [ 4 ]

5 голосов
/ 17 июня 2011

При использовании select,

use strict;
use warnings;

use IO::Select qw( );

sub process_msg {
    my ($client, $msg) = @_;
    chomp $msg;
    print "$client->{id} said '$msg'\n";
    return 1;  # Return false to close the handle.
}

my $select = IO::Select->new();
my %clients;

for (...) {
    my $fh = ...;
    $clients{fileno($fh)} = {
        id  => '...'
        buf => '',
        # ...
    };

    $select->add($fh);
}

while (my @ready = $select->can_read) {
    for my $fh (@ready) {
        my $client = $clients{ fileno($fh) };
        our $buf; local *buf = \( $client->{buf} );

        my $rv = sysread($fh, $buf, 64*1024, length($buf));
        if (!$rv) {
            if (defined($rv)) {
                print "[$client->{id} ended]\n";
            } else {
                print "[Error reading from $client->{id}: $!]\n";
            }

            print "[Incomplete message received from $client->{id}]\n"
                if length($buf);

            delete $clients{ fileno($fh) };
            $select->remove($fh);
            next;
        }

        while ($buf =~ s/^(.*\n)//) {
            if (!process_msg($client, "$1")) {
                print "[Dropping $client->{id}]\n";
                delete $clients{ fileno($fh) };
                $select->remove($fh);
                last;
            }
        }
    }
}
2 голосов
/ 17 июня 2011

Существует несколько вариантов.

  • readline или <$fh> вернет false при eof (или об ошибке).

  • eof вернет true при eof.

  • read (с размером блока> 0)возвращаемое значение определено и ноль для eof.

  • sysread (с размером блока> 0) вернет определенное значение и ноль для eof.

Вы можете использовать select или сделать ручку неблокируемой перед любым из вышеперечисленных проверок без блокировки.

2 голосов
/ 17 июня 2011

Что не так с ожиданием фактического EOF?

while (<$fh>) {
    ...
    sleep 1;
}

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

Есть ли другие вещи, которые вы хотите сделать, ожидая на some_long_running_proc?Если это так, select, вероятно, в вашем будущем.

1 голос
/ 17 июня 2011

Вы используете select (), чтобы выяснить, есть ли какие-либо данные, или исключительное условие, такое как закрытие.

Лично я предпочитаю использовать IO :: Multiplex, особенно когда вы мультиплексируете вход из несколькихразличные дескрипторы, но это может не применяться в этом случае.

...