7

Parallel::ForkManager を使用してフォークにある種のタイムアウト (時間制限) を実装することは可能ですか?

基本的な Parallel::ForkManager スクリプトは次のようになります

use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new( 10 );
for ( 1 .. 1000 ) {
    $pm->start and next;
    # some job for fork
    $pm->finish;
}
$pm->wait_all_children();

「#フォークのための仕事」の時間を制限したいと思います。たとえば、90 秒で終了しない場合。次に、それ(フォーク)を強制終了/終了する必要があります。これを使用することを考えましたが、Parallel::ForkManager で使用する方法がわかりません。

編集

Hobbsさん、池上さん、ありがとうございました。あなたの提案は両方ともうまくいきました.....しかし、私の実際のスクリプトではなく、この基本的な例でのみ:(. スクリーンショット これらのフォークは永遠にそこにあり、-正直に言うと-理由はわかりません。何も変更しませんでした (ただし、多くのことは外部変数に依存します)。すべてのフォークは Web サイトからページをダウンロードし、それを解析し、結果をファイルに保存する必要があります。フォークごとに 30 秒以上かかることはありません。タイムアウトは 180 秒に設定されていますぶら下がっているフォークは完全にランダムなので、問題を追跡するのは非常に困難です. そのため、一時的で簡単な解決策を思いついたのです - タイムアウト & キル.

私のコードでタイムアウトの方法を無効にする (中断する) 可能性があるのは何ですか? alarm()私のコードには他に何もありません。

編集2

フォークの 1 つが 1 時間 38 分ぶら下がっていて、「タイムアウト PID」を返しましdie()alarm()。したがって、タイムアウトは機能します...しかし、約1時間36,5分遅れます;)。あなたはなにか考えはありますか?

4

3 に答える 3

8

アップデート

終了後に更新して申し訳ありませんが、Parallel::ForkManager がrun_on_startコールバックもサポートしていることを指摘しなければ、私は気が進まないでしょう。time()これを使用して、PIDのスタンプを処理する「子の登録」機能をインストールできます。

例えば、

$pm->run_on_start(sub { my $pid = shift; $workers{$pid} = time(); });

結論run_on_waitとして、以下で説明するように、P::FM のメイン ループは特別なことをする必要はありません。つまり、単純な のままにすることができ$pm->start and next、コールバックが他のすべてを処理します。

元の回答

Parallel::ForkManager のrun_on_waitハンドラーと少しの簿記により、ハングしている ALRM 耐性のある子を強制的に終了させることができます。

その関数によって登録されたコールバックは、$pm子の終了を待っている間、定期的に実行できます。

use strict; use warnings;
use Parallel::ForkManager;

use constant PATIENCE => 90; # seconds

our %workers;

sub dismiss_hung_workers {
  while (my ($pid, $started_at) = each %workers) {
    next unless time() - $started_at > PATIENCE;
    kill TERM => $pid;
    delete $workers{$pid};
  }
}

...

sub main {
  my $pm = Parallel::ForkManager->new(10);
  $pm->run_on_wait(\&dismiss_hung_workers, 1);  # 1 second between callback invocations

  for (1 .. 1000) {
    if (my $pid = $pm->start) {
      $workers{$pid} = time();
      next;
    }
    # Here we are child.  Do some work.
    # (Maybe install a $SIG{TERM} handler for graceful shutdown!)
    ...
    $pm->finish;
  }

  $pm->wait_all_children;

}

(他の人が示唆しているように、 を介して子供たちに自分自身を調整させる方が良いですが、alarm()それは断続的に機能しないように見えます。また、各子供自身を持っているような無駄でひどいハックに頼ることもできますfork() or exec('bash', '-c', 'sleep 90; kill -TERM $PPID')。)

于 2012-06-11T04:22:17.133 に答える
4

必要なのは 1 行だけです。

use Parallel::ForkManager;
my $pm = Parallel::ForkManager->new( 10 );
for ( 1 .. 1000 ) {
    $pm->start and next;
    alarm 90;             # <---
    # some job for fork
    $pm->finish;
}
$pm->wait_all_children();

プロセスが終了することを意味するため、シグナルハンドラーを設定する必要はありません。

execお子様がいる場合でも機能します。Windows では動作しませんが、Windows での使用forkはそもそも疑問です。

于 2012-06-10T18:36:11.413 に答える
1

子プロセス内で、リンク先の回答が示唆することを実行するだけです(つまり$pm->start and next、ループの最後と最後の間です。Parallel::ForkManager とやり取りするために必要なことは特にありません。代わりに誤って親を殺します:)

于 2012-06-10T18:14:15.670 に答える