我正在寻找一种在Perl中实现读/写锁的好方法。 这需要在Windows和Unix上同步来自不同Perl线程和/或进程的文件访问。 尝试过Fcntl :: flock,如果按预期工作,对我来说是完美的。不幸的是,看起来在压力下flock允许在另一个线程中锁定已经锁定的文件。 查看了一些CPAN模块,但大多数都是用flock实现的。 接下来我打算评估Unix的win和Win32 :: Mutex的fcntl。 这似乎是一项非常常见的任务,也许我错过了一些简单的解决方案。 如果您知道任何问题,请指点一下吗?
谢谢!
答案 0 :(得分:9)
flock
不会在线程中执行您想要的操作。
您可以使用sysopen实现自己的锁定,如果文件在与O_EXCL|O_CREAT
一起使用时存在,则会失败。
一个例子,子进程竞争锁
use warnings;
use strict;
use feature 'say';
use Fcntl;
use Time::HiRes qw(sleep);
my $lock_file = ".lock.$$";
sub get_lock {
my ($file, $pid) = @_;
my $fh;
while (not sysopen $fh, $file, O_WRONLY|O_EXCL|O_CREAT) {
say "\t($$: lock-file exists ..)";
sleep 0.5;
}
say $fh $pid;
}
sub release_lock {
my ($file, $pid) = @_;
unlink $file or die "Error unliking $file: $!";
say "\t($$: released lock)";
}
my @pids;
for (1..4) {
my $pid = fork // die "Can't fork: $!";
if ($pid == 0) {
sleep rand 1;
get_lock($lock_file, $$);
say "$$, locked and processing";
sleep rand 1;
release_lock($lock_file, $$);
say "$$ completed.";
exit
}
push @pids, $pid;
}
wait for @pids;
最好使用File::Temp作为锁文件名,但要仔细阅读文档以了解细微之处。
包含3个进程的示例输出
3659, locked and processing (3660: lock-file exists ..) (3658: lock-file exists ..) (3659: released lock) 3659 completed. 3660, locked and processing (3658: lock-file exists ..) (3658: lock-file exists ..) (3660: released lock) 3660 completed. 3658, locked and processing (3658: released lock) 3658 completed.
在NFS下可能不支持O_EXCL
:您必须至少拥有2.6内核和NFSv3,否则会出现竞争条件。如果这是一个问题,解决方法是使用link(2)
获取锁定。请参阅man 2 open
(自sysopen
使用open
系统调用以来的其他详细信息)。
仅锁定文件访问权限,例如
sub open_with_lock {
my ($file, $mode) = @_;
get_lock($lock_file, $$);
open my $fh, $mode, $file or die "Can't open $file: $!";
return $fh;
}
sub close_and_release {
my ($fh) = @_;
close $fh;
release_lock($lock_file, $$);
return 1;
}
例如,可以将这些文件与get_lock
和release_lock
一起放在模块中,并将锁文件名称作为包全局。
一个简单的测试驱动程序
# use statements as above
use Path::Tiny; # only to show the file
my $lock_file = ".lock.file.access.$$";
my $file = 't_LOCK.txt';
my @pids;
for (1..4)
{
my $pid = fork // die "Can't fork: $!";
if ($pid == 0) {
sleep rand 1;
my $fh = open_with_lock($file, '>>');
say "$$ (#$_) opening $file ..";
say $fh "this is $$ (#$_)";
sleep rand 1;
close_and_release($fh);
say "$$ (#$_) closed $file.";
say '---';
exit;
}
push @pids, $pid;
}
wait for @pids;
print path($file)->slurp;
unlink $file;
使用第一个示例中的use
个语句和3个forks,运行
(18956: "lock"-file exists ..) # print out of order 18954 (#1) opening t_LOCK.txt ... (18955: "lock"-file exists ..) (18956: "lock"-file exists ..) (18955: "lock"-file exists ..) (18954: released lock) 18954 (#1) closed t_LOCK.txt. --- 18956 (#3) opening t_LOCK.txt ... (18955: "lock"-file exists ..) (18956: released lock) 18956 (#3) closed t_LOCK.txt. --- 18955 (#2) opening t_LOCK.txt ... (18955: released lock) 18955 (#2) closed t_LOCK.txt. --- this is 18954 (#1) this is 18956 (#3) this is 18955 (#2)
(请注意,独立进程正在争夺STDOUT
)