perl tk fileevent 在第二个事件上失败

perl tk fileevent fail on the second event

这个简单的摘录,运行 在 Perl 5.16.3 上,在打开时分叉了一个子进程,该子进程将数据发送到主进程。

#!/usr/bin/perl
use Tk;
my $mainWindow = MainWindow->new();
$mainWindow->Button(-text => "Run", -command => \&run)->pack();
MainLoop;
my $fh;
sub receiver()
{
    print "$_" while <$fh>;
    print "End\n";
    close $fh;
}
sub run()
{
    print "run\n";
    my $pid = open $fh, '-|';
    die "Failed to start\n" unless defined $pid;
    print "pid=$pid\n";
    exec "echo Hello" unless $pid;
    $mainWindow->fileevent($fh, 'readable', \&receiver);
}

仅第一次按预期工作,第二次单击打开按钮时产生:

Tk::Error: Can't locate object method "OPEN" via package "Tk::Event::IO" at fe.pl line 16. Tk callback for .button Tk::ANON at /usr/lib64/perl5/vendor_perl/Tk.pm line 251 Tk::Button::butUp at /usr/lib64/perl5/vendor_perl/Tk/Button.pm line 175 (command bound to event)

我正在努力理解原因,但没有成功。有什么想法吗?

Tk::Error: Can't locate object method "OPEN" via package "Tk::Event::IO"

似乎你需要在每次完成 receiver 时删除 fileevent 处理程序,否则 Tk 稍后将尝试再次调用回调(使用相同的您刚刚在接收器中关闭的文件句柄)但是文件句柄不再有效。

以下对我有用:

use strict;
use warnings;
use Tk;

my $mainWindow = MainWindow->new();
$mainWindow->Button(-text => "Run", -command => \&run)->pack();
MainLoop;

sub run()
{
    my $fh;
    my $receiver = sub {
        print "$_" while <$fh>;
        print "End\n";
        close $fh;
        # delete handler since $fh is no longer used
        $mainWindow->fileevent($fh, "readable", "");
    };

    print "run\n";
    my $pid = open $fh, '-|';
    die "Failed to start\n" unless defined $pid;
    print "pid=$pid\n";
    exec "echo Hello" unless $pid;
    $mainWindow->fileevent($fh, 'readable', $receiver);
}
#!/usr/bin/perl
use Tk;
my $mainWindow = MainWindow->new();
$mainWindow->Button(-text => "Run", -command => \&run)->pack();
MainLoop;
sub run()
{
    my $fh;
    my $pid;
    $SIG{CHLD} = sub
    {
        my $kid = waitpid(-1, WNOHANG);
        print "$kid exited with $? ${^CHILD_ERROR_NATIVE}\n"
    };
    my $receiver = sub
    {
        print "$_" while <$fh>;
        print "End\n";
        close $fh;
        # delete handler since $fh is no longer used
        $mainWindow->fileevent($fh, 'readable', '');
    };
    print "run\n";
    $pid = open $fh, '-|';
    die "Failed to start\n" unless defined $pid;
    print "pid=$pid\n";
    exec "echo Hello" unless $pid;
    $mainWindow->fileevent($fh, 'readable', $receiver);
}