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, 已锁定并正在处理
(3660: 锁文件已存在..)
(3658: 锁文件已存在..)
(3659: 已释放锁)
3659 已完成。
3660, 已锁定并正在处理
(3658: 锁文件已存在..)
(3658: 锁文件已存在..)
(3660: 已释放锁)
3660 已完成。
3658, 已锁定并正在处理
(3658: 已释放锁)
3658 已完成。
O_EXCL
可能在NFS下无法支持:您必须至少有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 Path::Tiny;
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个fork,在运行过程中:
(18956: "lock"-file exists ..) # 打印顺序错乱
18954 (#1) 打开 t_LOCK.txt ...
(18955: "lock"-file exists ..)
(18956: "lock"-file exists ..)
(18955: "lock"-file exists ..)
(18954: 释放锁)
18954 (#1) 关闭 t_LOCK.txt.
---
18956 (#3) 打开 t_LOCK.txt ...
(18955: "lock"-file exists ..)
(18956: 释放锁)
18956 (#3) 关闭 t_LOCK.txt.
---
18955 (#2) 打开 t_LOCK.txt ...
(18955: 释放锁)
18955 (#2) 关闭 t_LOCK.txt.
---
这是 18954 (#1)
这是 18956 (#3)
这是 18955 (#2)
(请注意,独立进程正在争夺STDOUT
)
flock
应该始终允许另一个线程获取锁,而不仅仅是在“紧张情况”下。 - ikegamiflock()
中描述符是独立处理的,而且“进程只能在文件上拥有一种类型的锁(共享或排他)。对已经锁定的文件进行后续的flock()
调用将会将现有锁转换为新的锁模式”。 - AndyHflock
可以在由文件描述符指定的打开文件上工作:“在由_fd_指定的打开文件上应用或删除一个咨询锁。” - ulix