4

多くの出力を複数のサブプロセスに送信する perl スクリプトがあります。すべてのパイプの終わりを閉じて、サブプロセスが作業を完了するのを待つ必要があります。これまでのところ、各パイプを閉じて、各サブプロセスが 1 つずつ終了するのを待つことに成功しただけです。より具体的には、次のようなことをしています。

for ($i=0;$i<24;$i++) {
    my $fh;
    open $fh, "|externalprogram $i";
    $fhs{$i}=$fh;
}

#...now I can write output to the pipes
while (moreworktodo()) {
    $whichone, $data = do_some_work();
    print $fhs{$whichone} $data;
}
#Now I just need to wait for all the subprocesses to finish.  However, they
#need to do a lot of work that can only begin when they've finished reading input.  So I need to close my end of the pipe to indicate I'm finished.
for ($i=0;$i<24;$i++) {
    my $file = $fhs{$i};
    close $file;  #unfortunately, this blocks until process $i finishes
    #meanwhile all the other processes are waiting for EOF 
    #on their STDIN before they can proceed.  So I end up waiting
    #for 24 processes to finish one-at-a-time instead of all at once
}

すべてのサブプロセスをすぐに終了させる (stdin を閉じる) 方法の 1 つは、(パイプ) ファイルハンドルをまったく閉じずにスクリプトを終了させることですが、スクリプトはサブプロセスを必要とするより大きなジョブの一部であるため、これは良くありません。先に進む前に実際に行う作業。

各サブプロセスの stdin を閉じて (それらがすべて作業を終了できるように)、次に進む前にすべてが終了するのを待つ簡単な方法は何ですか? 子を分岐して各パイプを閉じようとしましたが、うまくいかないようです。親の「閉じる」だけが実際にサブプロセスの stdin を閉じ、サブプロセスが終了するのを待ちます。

4

1 に答える 1

5

私はパイプを自分で作成し、使用しませんopen(P, "|external-program")。次に、子プロセスが終了するのを待たずにパイプを閉じることができます。

自分で子プロセスへのパイプを開く例:

sub spawn {
  my ($cmd) = @_;

  pipe(my $rp, $wp) or die "pipe failed: $!";

  my $pid = fork();
  die "fork: $!" unless defined($pid);
  if ($pid) {
    # parent
    close($rp);
    return ($wp, $pid);
  } else {
    # child
    close($wp);
    open(STDIN, "<&", $rp);
    exec($cmd) or die "exec: $!";
  }
}

sub main {
  $| = 1;
  my ($wp, $pid) = spawn("./child");
  for (1..10) {
    print {$wp} "sending $_\n";
  }
  close($wp);
  print "done\n";
 }

 main();

close()子が終了するのを待っていない、テストするサンプルの子プログラムを次に示します。

# file: ./child
while (<STDIN>) {
  print "got: $_";
  sleep(2);
}

パズルの最後のピースは、子プロセスが終了するのを非同期的に待機することです。これはハンドラーで行うことができます$SIG{CHLD}。あるいは、単純な「join_children」関数を次に示します。

my @child_ids = (1..24); # or whatever ids you want to use
my %pipe;                # hash map from child_id -> pipe handle

sub join_children {
  for my $id (@child_ids) {
    close( $pipe{$id} );
  }

  my $count = scalar(@child_ids);
  while ($count > 0) {
    wait;
    $count--;
  }
}
于 2012-11-23T00:51:53.853 に答える