2017-10-18 105 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