Perl:从后台线程关闭 open2 句柄

Perl: close open2 handle from a background thread

我只是想了解如何正确使用 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 );

我的问题是:


编辑:按照您的建议,使用 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. 您的程序已停止,因为它正在写入的管道已满。
  2. 通向 cat 的管道已满,因为 cat 停止读取它。
  3. cat 因为写入的管道已满而停止。
  4. 来自 cat 的管道已满,因为您的程序没有从中读取数据。

所以你有两个程序在等待对方做某事。这是一个死锁。

低级别的解决方案是使用select监控管道的两端。

高级解决方案是让 IPC::Run or IPC::Run3 为您完成这项艰巨的工作。

use IPC::Run qw( run );

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