在Perl中创建文件锁的最佳方式是什么?
是使用flock函数在文件上创建锁,还是创建一个锁文件并在锁文件上放置锁并检查锁文件上的锁更好?
如果您最终使用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' - $!";
一些有用的链接:
flock()
文档针对您添加的问题,我建议在文件上放置锁定或创建一个名为“lock”的文件,每当文件被锁定时调用它,并在不再锁定时将其删除(然后确保您的程序遵守这些语义)。
>>
而不是创建/覆盖符号 >
,有什么原因吗? - xagygflock仅支持文件级别的锁定,锁定只在单台机器内生效(您可以锁定NFS挂载的文件,但只有本地进程才能看到锁)。子进程会继承锁定状态(假设文件描述符没有关闭)。
有时候(SYSV系统),flock会使用lockf或fcntl进行模拟;在某些BSD系统中,lockf会使用flock进行模拟。一般来说,这些模拟方式的性能较差,建议尽量避免使用。
Ryan P写道:
在这种情况下,当文件重新打开时,文件实际上会短暂解锁。
所以不要这样做。相反,以读/写方式打开
文件:
open my $fh, '+<', 'test.dat'
or die "Couldn’t open test.dat: $!\n";
当你准备编写计数器时,只需将指针seek
移到文件的开头。注意,如果这样做,你应该在close
之前进行truncate
,以防止文件留下尾随垃圾,如果新内容比旧内容短。(通常,文件中的当前位置在其末尾,因此你可以直接写truncate $fh, tell $fh
。)
此外,请注意我使用了三个参数的open
和词法文件句柄,并检查操作的成功。 请避免全局文件句柄(全局变量不好,嗯?)和魔法两个参数的open
(它曾经是 Perl 代码中许多漏洞(可利用)的源头),并始终测试你的open
是否成功。
use Fcntl ':flock'; # 导入LOCK_*常量
# 打开文件进行追加 open (my $fh, '>>', 'test.dat') or die $!;
# 尝试独占锁定文件,将等待直到获得锁定 flock($fh, LOCK_EX);
# 在此处执行一些与文件有关的操作(在我们的情况下打印)
# 实际上你不应该解锁文件 # 关闭文件将解锁它 close($fh) or warn "Could not close file $!";
查看完整的flock文档和PerlMonks上的文件锁定教程,尽管那也使用旧的文件句柄用法。
实际上,我通常跳过close()的错误处理,因为如果失败了,我也没有太多可以做的。
关于锁定什么,如果您只在一个文件中工作,请锁定该文件。 如果您需要一次锁定多个文件,则为了避免死锁,最好选择一个要锁定的文件。 无论那个文件是您真正需要锁定的几个文件之一,还是专门为锁定目的创建的单独文件都没有关系。
您是否考虑过使用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' - $!";
我在这个问题中的目标是锁定一个被多个脚本用作数据存储的文件。最终,我使用了类似于以下代码(来自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
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);
基于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: $!";
}
除了使用锁文件的方法外,另一种替代方案是使用锁套接字。可以参考CPAN上的Lock::Socket实现。使用方法如下:
use Lock::Socket qw/lock_socket/;
my $lock = lock_socket(5197); # raises exception if lock already taken