Perl: закрыть дескриптор open2 из фонового потока - PullRequest
0 голосов
/ 02 января 2019

Я просто пытаюсь выяснить, как правильно использовать функцию open2.

См. Пример ниже.Это работает для небольшого $max, но, естественно, если я напишу достаточно долго в $hIn, то в итоге он будет заблокирован, потому что ничто не читает данные на выходе непрерывно.

use 5.26.0;
use IPC::Open2;
my $max = 100000;
my $pid = open2(my $hOut, my $hIn, "cat") || die "failed 'cat': $!";
{
    my $cnt = 0;
    #When $max is big (e.g. 100000) so the code below will get blocked
    #when writing to $hIn
    while ($cnt<$max) {say $hIn $cnt++;}
    close($hIn) || say "can't close hIn";
}
while(<$hOut>) { print; }
close($hOut) || say "can't close hOut";
waitpid( $pid, 0 );

ЕдинственноеРешение, о котором я могу подумать, - запустить другой поток, который будет писать в фоновом режиме.

С помощью приведенного ниже кода я могу записать в $hIn столько данных, сколько захочу, и прочитать их восновной поток, но $hIn, похоже, не закрывается.Из-за этого while(<$hOut>) никогда не закончится в ожидании большего вывода.

use 5.26.0;
use threads;
use IPC::Open2;
my $max = 100000;
my $pid = open2(my $hOut, my $hIn, "cat") || die "failed 'cat': $!";
my $thr = threads->create(sub {
    my $cnt = 0;
    while ($cnt<$max) {say $hIn $cnt++;}
    #The close does not have any effect here (although no error is produced)
    close($hIn) || say "can't close hIn";
});
#This outputs all the data written to $hIn but never leaves the loop...
while(<$hOut> ) { print; }
close($hOut) || say "can't close hOut";
$thr->join;
waitpid( $pid, 0 );

Мои вопросы:

  • При условии, что мой подход с потоками в порядке, как я могузакрыть дескриптор файла из фонового потока?
  • Если это не так (на самом деле use threads не рекомендуется в Perl), так может кто-нибудь предоставить рабочий пример open2, который может писать и читать много данных безриск быть заблокированным в ожидании читателя или писателя?

РЕДАКТИРОВАТЬ: Следуя вашим рекомендациям, здесь приведена реализация приведенного выше кода с использованием IPC :: Run:

use 5.26.0;
use IPC::Run qw/ run /;
my $max = 1000000;
run sub {
        my $cnt = 0;
        while ($cnt<$max) {say $cnt++;}
    },
    "|", "cat",
    "|", sub {
        while(<> ) {
            print;
        }
    }
    or die "run sub | cat | sub failed: $?";

Он работает без ошибок, код очень удобочитаемый ... Я очень рад, что узнал об этом модуле.Спасибо всем!

Тем не менее, я считаю этот вопрос без ответа.Если невозможно написать эту функцию, используя open2 напрямую, почему это вообще существует и сбивает с толку людей?Также невозможность закрыть дескриптор файла из другого потока выглядит для меня как ошибка (конечно, это так - закрытие должно как минимум сообщать об ошибке).

1 Ответ

0 голосов
/ 03 января 2019
  1. Ваша программа остановилась, потому что канал, в который она записывалась, заполнился.
  2. Канал к cat заполнился, потому что cat прекратил чтение из него.
  3. cat остановлен, потому что канал, в который он записывал, заполнился.
  4. Канал из cat заполнился, потому что ваша программа не читает из него.

Итак, у вас естьдве программы ждут друг друга, чтобы что-то сделать.Это тупик.

Низкоуровневое решение заключается в использовании select для контроля обоих концов канала.

Высокоуровневое решение позволяет IPC ::Выполнить или IPC :: Run3 сделать эту тяжелую работу за вас.

use IPC::Run qw( run );

my $cnt_max = 100000;
my $cnt = 0;
run [ "cat" ],
   '<', sub { $cnt < $cnt_max ? $cnt++ . "\n" : undef };
...