1

我正在使用 Perl“Tk”为外部命令(“sox”,如果有帮助的话)编写一个包装器。当然,我需要异步运行它,以避免阻塞 tk 的 MainLoop()。但是,我需要阅读它的输出以通知用户命令的进度。

我正在使用 IPC::Open3 测试这样的解决方案:

{
    $| = 1;
    $pid = open3(gensym, ">&STDERR", \*FH, $cmd) or error("Errore running command \"$cmd\"");
}
while (defined($ch = FH->getc)) {
    notifyUser($ch) if ($ch =~ /$re/);
}
waitpid $pid, 0;
$retval = $? >> 8;
POSIX::close($_) for 3 .. 1024; # close all open handles (arbitrary upper bound)

但是当然,while 循环会阻塞 MainLoop,直到 $cmd 确实终止。

有什么方法可以异步读取输出句柄吗?还是我应该使用标准叉子?该解决方案也应该在win32下工作。

4

1 回答 1

3

对于文件句柄的非阻塞读取,请查看Tk::fileevent.

这是一个示例脚本,如何将管道、分叉进程和文件事件一起使用:

use strict;
use IO::Pipe;
use Tk;

my $pipe = IO::Pipe->new;
if (!fork) { # Child XXX check for failed forks missing
    $pipe->writer;
    $pipe->autoflush(1);
    for (1..10) {
        print $pipe "something $_\n";
        select undef, undef, undef, 0.2;
    }
    exit;
}
$pipe->reader;

my $mw = tkinit;
my $text;
$mw->Label(-textvariable => \$text)->pack;
$mw->Button(-text => "Button", -command => sub { warn "Still working!" })->pack;
$mw->fileevent($pipe, 'readable', sub {
                   if ($pipe->eof) {
                       warn "EOF reached, closing pipe...";
                       $mw->fileevent($pipe, 'readable', '');
                       return;
                   }
                   warn "pipe is readable...\n";
                   chomp(my $line = <$pipe>);
                   $text = $line;
               });
MainLoop;

分叉可能会也可能不会在 Windows 下工作。在 Tk 内分叉时也需要小心;您必须确保只有两个进程中的一个在执行 X11/GUI 操作,否则会发生坏事(X11 错误、崩溃……)。一个好的方法是在创建 Tk MainWindow 之前进行 fork。

于 2013-07-19T14:34:03.407 回答