2016-08-22 9 views
4
use Parallel::ForkManager;  
my $number_running = 0; 
my $pm = new Parallel::ForkManager(30); 
$pm->run_on_start(sub { ++$number_running; }); 
$pm->run_on_finish(sub { --$number_running; }); 
for (my $i=0; $i<=100; $i++) 
{ 
    if ($number_running == 5) { while ($number_running > 0) {} } # waits forever 
    $pm->start and next; 
    print $i; 
    $pm->finish; 
} 

上記のコードでは、並列処理を使用してforループ内でコードを実行するためにParallel::ForkManagerを使用しています。実行中の子プロセスの数をカウントして、それに応じて$number_running変数を設定しています。 5つの子プロセスが実行されると、続行する前に0つの子プロセスが実行されるまで待つことを望みます。子プロセスが親プロセスで変数を設定するのを待つ方法はありますか?

forループの最初の行はこれを実現するように設計されていますが、その行は永遠に待機します。子プロセスによって作成された変数への変更は、そのコード行では利用できないようです。私は間違って何をしていますか?注:私はwait_all_childrenを認識していますが、私はそれを使用したくありません。

+0

あなたはそのようなアイドルループで回転しないでください。あなたの親プロセスは、取得できるCPU時間をすべて使い切り、 '$ number_running'の値を無限にテストします。 – Borodin

+0

'$ number_running'が子プロセスによってゼロに減少するまでどのように待つことができますか? – CJ7

+0

@ CJ7子プロセスは、親プロセスで使用される '$ number_running'には影響を与えません。親プロセスは、互いの変数に書き込むことができません。親はこの変数を減分しなければなりません。私の答えに加えられた説明を見てください。 – zdim

答えて

1

コールバックrun_on_startはすべての新しいプロセスで実行され、カウンタが増分されます。しかし、コールバックrun_on_finishは決してトリガされないので、カウンタは減分されません。従って、5に達すると、コードはwhileループ内に座ります。親と子は、お互いの変数を別々のプロセスで直接変更することはできないことに注意してください。

コールバックrun_on_finishは、すべてのプロセスがフォークされた後に通常wait_all_childrenになることによってトリガーされます。ジョブの最大数が実行され、1つが終了すると、ジョブは にも実行されます。これはwait_one_childon_finishを呼び出す、下記参照)によってstartで行われます。

それとも、これはこれは、子供を享受し、startまたはwait_all_childrenへの呼び出しの独立したコールバックを実行するための非ブロッキング呼び出しですreap_finished_children方法

を呼び出すことにより、自由に行うことができます。 startが頻繁に呼び出されないが、コールバックをすばやく実行したい場合に使用します。

これが(コメントで明らかなように)個々の子ども出口としてではなく、wait_all_childrenで通信する方法をの主な懸念を解決します。

ここでは、コールバックが子プロセスの終了時に正しく実行されるように使用する方法の例を示します。この方法の

​​

使用はfork -ing後、ループ内のwaitpid -1, POSIX::WNOHANGを呼び出すのと同じです。これは最大出力(30)よりもフォークして出力を見やすくし、子プロセスが終了するとコールバックが正しく実行されることを示します。これらの数字を変更して、完全な操作を確認します。

多くのコードは診断用です。出力の子を追跡するには、10*$iで終了します。匿名配列[...]に返されるデータは、同じ目的のための説明文字列です。 reap_finished_childrenが完了するとすぐに、$number_runningがコールバックで削減されます。このため、$curr変数が必要です(これも診断用です)。

これは直接の質問は全体のバッチが新しいものを開始する前に終了するのを待つためにどのようである

 
start: Started 4656, running: 1 
start: Started 4657, running: 2 
start: Started 4658, running: 3 
Running: 4656 4658 4657 
     Kid #1 slept for 1, exiting 
Cleared 4656, kid #1 exited with 10 
Remains: 2. Data: gone-4656 
     Kid #2 slept for 10, exiting 
Cleared 4657, kid #2 exited with 20 
Remains: 1. Data: gone-4656 gone-4657 
     Kid #3 slept for 20, exiting 
Cleared 4658, kid #3 exited with 30 
Remains: 0. Data: gone-4656 gone-4657 gone-4658 

を印刷します。これは、wait_for_available_procs($n)

利用可能なプロセススロットが利用可能になるまで待つことによって直接行うことができます。 $nが指定されていない場合、デフォルトはになります。

$MAX$nに使用されている場合、多くのスロットはバッチ全体が完了した時点でのみ使用可能になります。 $nの使用方法は、実行時に決定することもできます。


モジュールの操作のいくつかの詳細

子はSIGCHLD信号が、それは子供がなくなっていることを知っている(と避けるためにキャッチする必要があります親に送られ、終了するとゾンビ、最初に)。これは、waitまたはwaitpidをコード内またはSIGCHLDハンドラ(ただし1か所のみ)で実行します。 fork,Signals in perlipc,waitpidおよびwaitを参照のこと。我々は、方法wait_all_children

sub wait_all_children { my ($s)[email protected]_; 
    while (keys %{ $s->{processes} }) { 
    $s->on_wait; 
    $s->wait_one_child(defined $s->{on_wait_period} ? &WNOHANG : undef); 
    }; 
} 

で使用さ

sub wait_one_child { my ($s,$par)[email protected]_; 
    my $kid; 
    while (1) { 
    $kid = $s->_waitpid(-1,$par||=0); 
    last if $kid == 0 || $kid == -1; # AS 5.6/Win32 returns negative PIDs 
    redo if !exists $s->{processes}->{$kid}; 
    my $id = delete $s->{processes}->{$kid}; 
    $s->on_finish($kid, $? >> 8 , $id, $? & 0x7f, $? & 0x80 ? 1 : 0); 
    last; 
    } 
    $kid; 
}; 

_waitpidサブ介して)これはwait_one_childで行われることP::FM's sourceから参照

上記で使用された0はこのメソッドの同義語です。

シグナルを受け取るメソッドwait_one_childは、最大プロセス数が満たされ、1つが終了すると、子プロセスを収穫するために、startによって使用されます。これは、モジュールがいつ他のプロセスを開始してその最大値を尊重することができるかをモジュールが知る方法です。 (プロセスを待ついくつかの他のルーチンでも使用されています。 )。 run_on_finishがトリガされますとき、これは$s->on_finish($kid, ...)

sub on_finish { 
    my ($s,$pid,@par)[email protected]_; 
    my $code=$s->{on_finish}->{$pid} || $s->{on_finish}->{0} or return 0; 
    $code->($pid,@par); 
}; 

コールバックは、それ自体がサブrun_on_finishに設定されているオブジェクトのon_finishキーから取得コードリファレンス$code、であることで、あります。これがコールバックが設定される方法です。

このためにユーザに提供される方法は、wait_all_childrenreap_finished_childrenです。

投稿コードにはこれが使用されていないため、$number_runningは更新されないため、whileは無限ループです。 の変数$number_runningは、子プロセスによって直接変更することはできません。

+0

'run_on_finish'は、子プロセスが終了するたびに実行されます。 '$ number_running'は子プロセスによって更新されます。私はこれを自分でテストしました。私は 'wait_for_children'を使いたくありません。私は '$ number_running'が子プロセスによってゼロに減少するのを待つ方法を知りたいです。 – CJ7

+0

@ CJ7 1.子供はカウンターとは関係ありません。子コードは、 'start'の後に入力するものです。あなたは子供のために待っています(つまり 'waitpid')、それは分岐の最初のルールです。 3.プログラムは、子どもが退出したことを実際に知るために、キャッチ(および対処)しなければならない信号を介して、子供の退出を知らされる。もう1つの方法は、子が ''パイプで戻ってくる(または 'SIGUSR'信号を送る)ことで、それがextiingだと言うことですが、そうでない場合は子コードをタイプします。 4.最終的にソースコードを見てみると、このwaitはwait_all_childrenで実際に行われます。私は私のポストを更新します。 – zdim

+0

@ CJ7 'while'は明らかに無限です。これは、子プロセスが作成された直後に終了するにもかかわらず、' $ number_running'が更新されないためです。これは、コールバックが実行されないことが原因である可能性があります。私が 'wait_all_children'をコメントアウトするとき、私はそれの確認を見る - すべてのプロセスがフォークされた後にのみ、' Cleared PID'行が印刷され始める。 – zdim

関連する問題