3

我在看AnyEvent::Fork模块。我有 20 个外部脚本我想并行调用(一次 6 个),并在全部完成后总结它们的输出。我不知道如何实现这一目标。

模块中的示例代码(仅调用 1 个子代码)有问题。我只是在代码中添加了一个简单的睡眠,不会立即返回,并且父进程会立即退出,而无需等待子进程。

open my $output, ">/tmp/log" or die "$!";

AnyEvent::Fork
   ->new
   ->eval ('
        # compile a helper function for later use
        sub run {
           my ($fh, $output, @cmd) = @_;

           # perl will clear close-on-exec on STDOUT/STDERR
           open STDOUT, ">&", $output or die;
           open STDERR, ">&", $fh or die;

           ### Added by me to demonstrate that
           ### $cv->recv returns immediately.
           sleep 5;

           exec @cmd;
        }
     ')
   ->send_fh ($output)
   ->send_arg ("/bin/echo", "hi")
   ->run ("run", my $cv = AE::cv);

my $stderr = $cv->recv;

结果是/tmp/log空的。我不明白condvar这里是如何使用的,它不在文档中。我可以得到正在运行的孩子的数量condvar吗?

请帮助如何解决这个问题。

更新这里的主要问题是父母不等待孩子完成。

4

1 回答 1

4

这里的问题是父进程和子进程是独立的进程,并且可以彼此独立运行,因此如果一个应该等待另一个,则需要显式同步。有很多方法可以做到这一点,最简单的方法是使用 AnyEvent::Fork::RPC,然后向孩子发送一个“等待”请求,它会在完成后回复。

要使用裸 AnyEvent::Fork 做到这一点,最简单的方法是利用 ->run 提供的双向管道:

  AnyEvent::Fork
    ->new
    ->run (sub {
       my ($fh) = @_;
       sysread $fh, my $dummy, 1; # will wait for data, or eof
       ... done, you can now call e.g. $cv->send

sysread 将尝试从孩子那里读取。如果孩子从不发送任何东西,这将阻止父母直到孩子退出,因为在那一刻,孩子将关闭它的管道末端并且 sysread 得到一个 EOF。

当然,在 AnyEvent 程序中您可能不想阻塞,因此您使用 I/O 观察程序:

 ->run (sub {
    my ($fh) = @_;
    my $rw; $rw = AE::io $fh, 0, sub {
       ... read data received, or EOF
       undef $rw;
       ... done, you can now call e.g. $cv->send;
    }
  });

这个技巧也可以用于外部命令(在你的例子中是 exec),通过清除子管道的 close-on-exec 状态,从而将它传递给被执行的程序 - 在这种情况下,当所有程序继承管道出口,管道将发出EOF信号。

这应该让你开始。还有其他方法可以做到这一点,但大多数甚至所有好的方法都会涉及一些通信管道,而最容易获得的方法是使用 AnyEvent::Fork 提供的方法。

于 2014-11-30T23:34:02.010 回答