Perlでファイルにロックを作成する最良の方法は何ですか?
ファイルを群がらせるか、ロックファイルを作成してロックを配置し、ロックファイルのロックを確認するのが最善ですか?
flock を使用することになった場合は、次のコードを使用します。
use Fcntl ':flock'; # Import LOCK_* constants
# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code. Use a variable.
my $file = '/path/to/some/file';
# Open the file for appending. Note the file path is quoted
# in the error message. This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";
# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX) or die "Could not lock '$file' - $!";
# Do something with the file here...
# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above. This could create
# a race condition. The close() call below will unlock the
# file for you, but only after writing any buffered data.
# In a world of buffered i/o, some or all of your data may not
# be written until close() completes. Always, always, ALWAYS
# check the return value of close() if you wrote to the file!
close($fh) or die "Could not write '$file' - $!";
いくつかの便利なリンク:
あなたの追加の質問に答えて、ファイルにロックをかけるか、ファイルがロックされているときはいつでも「ロック」と呼ばれるファイルを作成し、ロックが解除されたら削除します(そして、プログラムが従うことを確認しますそれらのセマンティクス)。
他の回答は Perl のフロック ロックをかなりうまくカバーしていますが、多くの Unix/Linux システムには、実際には 2 つの独立したロック システムがあります: BSD flock() と POSIX fcntl() ベースのロックです。
Perl をビルドするときに設定する特別なオプションを指定しない限り、その群れは利用可能な場合 flock() を使用します。これは通常は問題なく、アプリケーション内でロックする必要がある場合 (単一のシステムで実行されている場合) にはおそらく必要です。ただし、fcntl() ロックを使用する別のアプリケーション (多くのシステムでの Sendmail など) と対話する必要がある場合や、NFS マウントされたファイルシステム全体でファイルをロックする必要がある場合があります。
そのような場合は、File::FcntlLockまたはFile:: lockf を確認することをお勧めします。純粋な Perl で fcntl() ベースのロックを行うこともできます (pack() の毛むくじゃらで移植性のないビットを使用)。
flock/fcntl/lockf の相違点の概要:
ほとんどの場合、lockf は fcntl の上に実装され、ファイル レベルのロックしかありません。fcntl を使用して実装した場合、以下の制限が lockf にも適用されます。
fcntl は範囲レベルのロック (ファイル内) と NFS 経由のネットワーク ロックを提供しますが、ロックは fork() の後に子プロセスに継承されません。多くのシステムでは、ファイルハンドルを読み取り専用で開いて共有ロックを要求し、読み取り/書き込みで排他ロックを要求する必要があります。
flock にはファイルレベルのロックのみがあり、ロックは単一のマシン内でのみ行われます (NFS マウントされたファイルをロックできますが、ローカル プロセスのみがロックを認識します)。ロックは子によって継承されます (ファイル記述子が閉じられていないと仮定します)。
時々 (SYSV システム) flock は lockf または fcntl を使用してエミュレートされます。一部の BSD システムでは、lockf は flock を使用してエミュレートされます。一般に、この種のエミュレーションはうまく機能しないため、回避することをお勧めします。
レスキューへの CPAN: IO::LockedFile。
Ryan P さんが書きました:
この場合、ファイルが再度開かれている間、ファイルは実際には短時間ロック解除されます。
だから、それをしないでください。代わりに、open
読み取り/書き込み用のファイル:
open my $fh, '+<', 'test.dat'
or die "Couldn’t open test.dat: $!\n";
カウンターを書き込む準備ができたらseek
、ファイルの先頭に戻ります。これを行う場合は、新しいコンテンツが以前のコンテンツよりも短い場合にファイルの末尾にゴミが残らないように、 のtruncate
直前にする必要があることに注意してください。close
(通常、ファイル内の現在の位置はファイルの最後にあるため、そのまま記述できますtruncate $fh, tell $fh
。)
open
また、3 つの引数とレキシカル ファイル ハンドルを使用し、操作の成功も確認したことに注意してください。グローバル ファイル ハンドル (グローバル変数はよくないですよね?) と魔法の 2 つの引数open
(Perl コードの多くの (悪用可能な) バグの原因となっています) を避け、常にopen
s が成功するかどうかをテストしてください。
これをファイルハンドラーおよびエラー処理として字句変数を使用して表示する方がはるかに良いと思います。また、すべてのオペレーティングシステムで正しい数値ではない可能性があるマジックナンバー2をハードコーディングするよりも、Fcntlモジュールの定数を使用することをお勧めします。
Fcntl':flock';を使用します。#LOCK_*定数をインポートします #追加するファイルを開く open(my $ fh、'>>'、'test.dat')またはdie $ !; #ファイルを排他的にロックしようとします。ロックが取得されるまで待機します flock($ fh、LOCK_EX); #ここのファイルで何かをします(この場合はファイルに印刷します) #実際にはファイルのロックを解除しないでください #ファイルを閉じるとロックが解除されます close($ fh)または「ファイル$を閉じることができませんでした!」と警告します。
古いスタイルのファイルハンドルの使用法も使用している場合でも、 flockの完全なドキュメントとPerlMonksのファイルロックチュートリアルを確認してください。
とにかく失敗した場合にできることはあまりないので、実際には通常、close()でのエラー処理をスキップします。
ロックする対象については、単一のファイルで作業している場合は、そのファイルをロックします。一度に複数のファイルをロックする必要がある場合は、デッドロックを回避するために、ロックしているファイルを1つ選択することをお勧めします。それが本当にロックする必要のあるいくつかのファイルの1つであるか、ロックの目的で作成する別のファイルであるかは、実際には問題ではありません。
LockFile::Simple モジュールの使用を検討しましたか? ほとんどの作業はすでに行われています。
私の過去の経験では、非常に使いやすく、頑丈であることがわかりました。
use strict;
use Fcntl ':flock'; # Import LOCK_* constants
# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code. Use a variable.
my $file = '/path/to/some/file';
# Open the file for appending. Note the file path is in quoted
# in the error message. This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";
# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX);
# Do something with the file here...
# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above. This could create
# a race condition. The close() call below will unlock it
# for you, but only after writing any buffered data.
# In a world of buffered i/o, some or all of your data will not
# be written until close() completes. Always, always, ALWAYS
# check the return value on close()!
close($fh) or die "Could not write '$file' - $!";
http://metacpan.org/pod/File::FcntlLockから開発
use Fcntl qw(:DEFAULT :flock :seek :Fcompat);
use File::FcntlLock;
sub acquire_lock {
my $fn = shift;
my $justPrint = shift || 0;
confess "Too many args" if defined shift;
confess "Not enough args" if !defined $justPrint;
my $rv = TRUE;
my $fh;
sysopen($fh, $fn, O_RDWR | O_CREAT) or LOGDIE "failed to open: $fn: $!";
$fh->autoflush(1);
ALWAYS "acquiring lock: $fn";
my $fs = new File::FcntlLock;
$fs->l_type( F_WRLCK );
$fs->l_whence( SEEK_SET );
$fs->l_start( 0 );
$fs->lock( $fh, F_SETLKW ) or LOGDIE "failed to get write lock: $fn:" . $fs->error;
my $num = <$fh> || 0;
return ($fh, $num);
}
sub release_lock {
my $fn = shift;
my $fh = shift;
my $num = shift;
my $justPrint = shift || 0;
seek($fh, 0, SEEK_SET) or LOGDIE "seek failed: $fn: $!";
print $fh "$num\n" or LOGDIE "write failed: $fn: $!";
truncate($fh, tell($fh)) or LOGDIE "truncate failed: $fn: $!";
my $fs = new File::FcntlLock;
$fs->l_type(F_UNLCK);
ALWAYS "releasing lock: $fn";
$fs->lock( $fh, F_SETLK ) or LOGDIE "unlock failed: $fn: " . $fs->error;
close($fh) or LOGDIE "close failed: $fn: $!";
}
この質問での私の目標は、いくつかのスクリプトのデータ ストアとして使用されているファイルをロックすることでした。最後に、次のようなコードを使用しました(Chrisから):
open (FILE, '>>', test.dat') ; # open the file
flock FILE, 2; # try to lock the file
# do something with the file here
close(FILE); # close the file
彼の例では、close(FILE) もこのアクションを実行するため、群れ FILE, 8 を削除しました。本当の問題は、スクリプトの開始時に現在のカウンターを保持する必要があり、終了時にカウンターを更新する必要があることでした。これは、Perl が問題を抱えている場所です。ファイルを読み取るには、次のようにします。
open (FILE, '<', test.dat');
flock FILE, 2;
今、結果を書き出したいのですが、ファイルを上書きしたいので、再度開いて切り捨てる必要があります。その結果、次のようになります。
open (FILE, '>', test.dat'); #single arrow truncates double appends
flock FILE, 2;
この場合、ファイルが再度開かれている間、ファイルは実際には短時間ロック解除されます。これは、外部ロック ファイルの場合を示しています。ファイルのコンテキストを変更する場合は、ロック ファイルを使用します。変更されたコード:
open (LOCK_FILE, '<', test.dat.lock') or die "Could not obtain lock";
flock LOCK_FILE, 2;
open (FILE, '<', test.dat') or die "Could not open file";
# read file
# ...
open (FILE, '>', test.dat') or die "Could not reopen file";
#write file
close (FILE);
close (LOCK_FILE);
ロックファイルアプローチに代わる方法の 1 つは、ロックソケットを使用することです。そのような実装については、CPAN のLock::Socketを参照してください。使い方は以下のように簡単です。
use Lock::Socket qw/lock_socket/;
my $lock = lock_socket(5197); # raises exception if lock already taken
ソケットを使用する利点はいくつかあります。
もちろん、明らかな欠点は、ロック名前空間がグローバルであることです。別のプロセスが必要なポートをロックすることを決定した場合、一種のサービス拒否が発生する可能性があります。
[開示:私は前述のモジュールの作成者です]
flock は Unix スタイルのファイル ロックを作成し、Perl が実行されているほとんどの OS で使用できます。ただし、flock のロックは助言のみです。
編集:群れが移植可能であることを強調