電子書籍の厳選無料作品が豊富!

おそれいります。
親プロセスから子プロセスを複数呼び
子プロセスが全て終わった後に次の手順に移行する。。。という処理をflockを使って作っているのですが、
うまくいきません。

下の「main.pl」を実行すると、「lock.pl」を10回呼び出して、

すべて終了したら「OK」と表示したいです。

lockというファイルを全部のプロセスで共有して、
子の全部の処理が終わった時点で、mailにロックが移る。。。という動きを想定したのですが、
なぜか、数回実行しているとMAIN LOCK(1) が最後に発動しているにもかかわらず、
子プロセスが終了していないのに「OK」の表示を出してしまいます。


flockは順番を守らないのでしょうか・・・・?????

どのようにしたらうまく動くでしょうか。。。。



==> main.pl <==
#!/usr/bin/perl

use Fcntl ':flock';

for (1..10){
system("./lock.pl $_ &");
}

sleep 5; # 子プロセスが開始するまで待つ

print qq("MAIN LOCK\n"); # ... (1)
open LC,"+<lock" or die;
flock LC,LOCK_EX;
sleep 1;
print qq("OK\n");




==> lock.pl <==
#!/usr/bin/perl

use Fcntl ':flock';

print qq(lock $ARGV[0]\n!); # ... (2)
open LC,"+<lock" or die;
flock LC,LOCK_EX;
sleep $ARGV[0];
print qq(release $ARGV[0]!\n);

A 回答 (4件)

質問の趣旨に合っているかわかりませんが、プログラムを作ってみました。

ロックした後の処理に時間がかからなければ、順番どおりに実行されると思います。

main.pl
--------------------------
#!/usr/bin/perl
foreach (1 .. 10) {
system("./lock.pl $_ &");
}

lock.pl
--------------------------
#!/usr/bin/perl
use Fcntl ':flock';
use POSIX ':sys_wait_h';
$arg = $ARGV[0];
sleep $arg;
$pid = fork();
die "Can't fork: $!" unless defind $pid;

if ($pid) {
do { $kid = waitpid(-1, &WNOHANG); } until $kid == -1;
if ($arg == 10) {
print "MAIN LOCK\n";
open LC, "+<lock" or die;
flock LC, LOCK_EX or die;
print "OK\n";
}
} else {
print "lock $arg\n";
open LC, "+<lock" or die;
flock LC, LOCK_EX or die;
print "release $arg\n";
}
    • good
    • 0
この回答へのお礼

おお!
サンプルありがとうございます!

とりあえず、なるべく現行を変えないでということで、
子スクリプトで実行時にフラグファイルを作るようにして、
そのフラグを親スクリプトが発見するまで待つようにしてみました。


for ( 0 .. 19 ){
system("[子スクリプト] $_ &"); # ←この引数$_を子スクリプトでフラグとして作成
for( 0 .. 600 ){
if( -f $_ ){
last;
}
sleep 1;
}
}


こんな感じにしたら、親のロックが最後になるようになったため
大丈夫になりました!

ループを600にしてるのは、600秒も開始しない場合はなにかおかしいだろうということで
ここには書きませんでしたが、exit 1 するようにしました。

※ しかしながら、この子供って結構重い処理なのですが、6分ぐらい開始しない、ということがありました。
  sparcのsolarisではそんなことなかったので、ちょっと驚きました。

お礼日時:2011/04/26 16:32

No1 です。



> system("./lock.pl $_ &");

上記のように書いた場合は、制御が非常に難しいと思います。& がなければ親プロセス (PPID) は main.pl になりますが、& を付けた場合は main.pl が親プロセスにならず切り離されてしまうからです (私が使っている Linux の場合)。
    • good
    • 0
この回答へのお礼

> 上記のように書いた場合は、制御が非常に難しいと思います。

そのようですね。
実はこのやり方はsolarisで試行錯誤をしてうまく行ったやり方だったのですが、
Linuxであっさりうまくいかなくなり、ご質問させていただきました。
(cygwinでもダメでした・・・)

今あらためて、solarisだと動きは素直でした。。。

お礼日時:2011/04/19 09:23

プロセスがどのようなタイミングで動くはOSのスケジューラ次第です。


親プロセスのCPUタイムに余裕があるなら、forループに続けて、ロックの獲得・解放まで処理が進みます。
なので、
>子の全部の処理が終わった時点で、mailにロックが移る。。。
と思ったのが敗因かと。

子プロセスとの同期には、waitpidやsigchildなど別の仕掛けがありますので、そちらを使ってみては。

この回答への補足

forループのあとに5秒おいて、必ず子が先に始まるようにしているのですが、
ダメなんですよね・・・
なので、仰っていることの半分くらいはわかるのですが、
>forループに続けて、ロックの獲得・解放まで処理が進みます。
はprint文を入れてログを出してみてもちゃんと最後に(1)が実行されているのですが・・・

>waitpidやsigchildなど別の仕掛け

そうなんですね。調べてみます。
ありがとうございました。

補足日時:2011/04/18 23:37
    • good
    • 0

> system("./lock.pl $_ &");


  ↓
 system("./lock.pl $_");

通常は system 関数が終わるまで待ちますが、バックグラウンドで子プロセスを起動しているため終了を待たないためと思います。& を消せば、すべての sleep がなくても順番どおりに実行されるはずです。
    • good
    • 0
この回答へのお礼

回答ありがとうございました。
説明不足で申し訳ありません。
多重で実行したいので&をつけております。

お礼日時:2011/04/18 23:32

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!