2017-10-18 97 views
4

我正在寻找一种在Perl中实现读/写锁的好方法。 这需要同步来自Windows和Unix上不同Perl线程和/或进程的文件访问。 试过Fcntl :: flock这对我来说是完美的,如果它按预期工作。不幸的是,它看起来像在压力下,flock允许在另一个线程中设置锁定已经锁定的文件。 看着一些CPAN模块,但大多数都是用群集来实现的。 接下来,我打算评估针对Windows的fcntl for Unix和Win32 :: Mutex。 这似乎是一个很常见的任务,也许我错过了一些简单的解决方案。 如果您知道有任何问题,请您为我指出一下吗?Perl中的读写锁

谢谢!

+0

哼声,'flock'应该总是让另一个线程获得一个锁,而不仅仅是“处于压力之下”。 – ikegami

+0

如果每个线程都有自己的文件句柄,是否为真? – AndyH

+0

https://linux.die.net/man/2/flock提供了更多细节,说明在相同的进程和相同或不同的fd内,鸡群如何以及何时能够成功。 – ulix

回答

7

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. 

O_EXCL输出可能NFS下unsuppored:你必须有至少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_lockrelease_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; 

与从第一实施例,并用3个叉use语句,一个运行

 
     (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

+1

NFS也是'flock'存在的祸根。 – mob

+0

@mob sigh ...我在NFS下面工作:(。当然有好东西(但是这些对我来说并不经常发生) – zdim

+0

@AndyH添加了锁定文件访问的具体示例 – zdim