Perlでの排他制御(ロック)

[上に] [前に] [次に]
satoshi 1998/07/31(金) 12:35:30
Perlでの排他制御(ロック)の話を
http://www.cup.com/negi/tips/flock.html
に書きました。ご意見ご感想を寄せてください。

mo [E-Mail] 1998/08/03(月) 19:06:16
以前、fj でも話題になりましたが、perl で close 前に flock(FIZ, 8)
でロックを解除すると、思わぬ尾とし穴に引っかかりかねないので
flock(FIZ, 8) は注意したほうがいいです。ちょっと古い perl
(5.003_25 以前だったかな?)の場合はバッファリングのために排他制御
がうまくいかないことがあるからです。よく見かける典型的な誤りは、

open(FIZ, ">>...");  # 出力ファイルを開く
flock(FIZ, 2);       # ここから排他処理
seek(FIZ, 0, 2);     # 基本 (^^)。ここはこれでよし。
print FIZ, "hello\n";# FIZ に "hello" を出力。でも実際は
                     # バッファに書き込まれ、ファイルには書き出されない。
flock(FIZ, 8);       # ロックを解除、ここまで排他処理
close(FIZ);          # ここで初めてファイルに書き出される。

となってしまい、ロック解除後に書き込んでしまうのです。perl 5.004 なら
flock の直前でバッファがフラッシュされ、上記コードで別に何の問題もないで
気にする必要はないです。古い perl も考慮にいれるなら、

open(FIZ, ...);
flock(FIZ, 2);
# do something
close(FIZ);

のように、flock(FIZ, 8) を呼ばないで直接 close するか、あるいは、

open(FIZ, ...);
flock(FIZ, 2);
# do something
$orig = select(FIZ); $| = 1; select($orig);
flock(FIZ, 8);
close(FIZ);

のように、バッファをフラッシュしてからロックオペレーションを
行なったほうがいいです。

とほほ 1998/08/04(火) 00:22:10
今ふと気づいたのですが、flock(FIZ,8); の前に print FIZ ""; と
かの処理を入れなくても大丈夫でしょうか?

satoshi 1998/08/08(土) 23:52:06
おお、ありがとうございます。記述に加えました。

ひろぼー 1998/08/09(日) 21:12:17
ちょっと質問してもいいですか?
チャットや掲示板などで、
以下のような書き込みロック方法をたまに見かけるのですが、
上記を読んで行くと、全く意味がないってことでしょうか?(^^;

open(LOG,">logfile.txt");
flock(LOG,2);
print LOG, "処理結果";
flock(LOG,8);
close(LOG);

flockが紳士協定なら、他のプロセスからロックがかかってても、
flock検査する前にopenしちゃうから、すべて消えちゃう?

B-Cus 1998/08/09(日) 23:49:48
openしたからといって、すぐにファイルに書き込まれるわけでは
ありません。書き込まれるのは、ファイルハンドルにデータを
送って、かつバッファがフラッシュされたときです。

> 上記を読んで行くと、全く意味がないってことでしょうか?(^^;

ですから大丈夫です。

とほほ 1998/08/10(月) 01:47:51
そう言われてみれば、open(LOG, ">logfile.txt"); を実行した時点
で、すでに trancate(LOG, 0);が実行されてしまいそうな気もします
ね。close(LOG)の前にflock(LOG,8);を行うのも問題があるし・・・

open(LOG, ">>logfile.txt");
flock(LOG, 2);
truncate(LOG, 0);
print LOG "処理結果";
close(LOG);

が正しい使用方法なのでしょうか。

mo [E-Mail] 1998/08/10(月) 15:31:31
いや、
open(LOG, ">logfile.txt");
のままでもいいんじゃないのかな?
これって、C 言語で
fd = open("logfile.txt", O_WRONLY | O_TRANC | O_CREAT);
として実行されますよね。O_WRONLY | O_TRANC の意味って、たしか
「上書きで open した後に、ファイルの長さを 0 にする」
っていうことだったと思いますよ。だから、

> open(LOG,">logfile.txt");
> flock(LOG,2);

を複数のプロセスが同時に呼び出したとしても、ちゃんと排他制御が
行なわれるはずです。

ひろぼー 1998/08/10(月) 19:33:56
実験してみました。確かに排他制御は行われるのですが・・・

【第一プロセス】
open(LOG, ">log.txt");
flock(LOG,2);
print LOG "ABCD\n";
sleep 10;
close(LOG);

【第二プロセス】
open(LOG, ">log.txt");
flock(LOG,2);
print LOG "A\n";
close(LOG);

第一プロセス実行直後に第二プロセスを実行すると、
【log.txt結果】
A
CD

なんてことになりますね。
第二プロセスが open で0バイトにしても、
第一プロセスの close が長さを書き換えてしまう。
やはり truncate が必要なのか・・・
flock が使えて truncate が使えないことってあるのかな?

直接ロックは動作の予測が難しいですね・・・

とほほ 1998/08/10(月) 23:24:42
ロックしていないのに「ファイルの長さを0にする=ファイルの中身を
変更する」とまずいですよね。やはり、truncate()を用いたコードが
正しい用法だと思います。

B-Cus 1998/08/11(火) 00:32:33
> 実験してみました。確かに排他制御は行われるのですが・・・
(snip!)

あら、ほんとだ。確かめずにレスしちゃってごめんなさい。

[上に] [前に] [次に]