【问题标题】:Read-write lock in PerlPerl 中的读写锁
【发布时间】:2018-03-30 19:03:56
【问题描述】:

我正在寻找一种在 Perl 中实现读/写锁的好方法。 这是在 Windows 和 Unix 上同步来自不同 Perl 线程和/或进程的文件访问所必需的。 尝试了 Fcntl::flock 如果它按预期工作对我来说将是完美的。不幸的是,在压力下,flock 似乎允许在另一个线程中对已锁定的文件设置锁定。 研究了一些 CPAN 模块,但大多数都是用flock实现的。 接下来我计划评估 fcntl 用于 Unix 和 Win32::Mutex 用于 Windows。 这似乎是一项非常常见的任务,也许我缺少一些简单的解决方案。 有知道的可以帮我指出来吗?

谢谢!

【问题讨论】:

  • 嗯,flock 应该始终允许另一个线程获得锁,而不仅仅是“处于压力下”。
  • 如果每个线程都有自己的文件句柄,这是真的吗?
  • linux.die.net/man/2/flock 提供了更多关于群如何以及何时在相同进程和相同或不同 fd 中成功的详细信息。
  • 有点不清楚:“flock() 独立处理描述符”和“一个进程只能在文件上持有一种类型的锁(共享或独占)。对已锁定文件的后续flock() 调用会将现有锁定转换为新锁定模式。
  • 那么,在同一个进程中,当文件被另一个线程通过单独的文件描述符锁定时,是否应该阻塞一个线程?

标签: multithreading perl


【解决方案1】:

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_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;

使用第一个示例中的 use 语句和 3 个分叉,运行一次

(18956: "lock"-file exists ..) # 打印乱序 18954 (#1) 打开 t_LOCK.txt ... (18955:“锁定”文件存在..) (18956:“锁”-文件存在..) (18955:“锁定”文件存在..) (18954:释放锁) 18954 (#1) 关闭 t_LOCK.txt。 --- 18956 (#3) 打开 t_LOCK.txt ... (18955:“锁定”文件存在..) (18956:释放锁) 18956 (#3) 关闭 t_LOCK.txt。 --- 18955 (#2) 打开 t_LOCK.txt ... (18955:释放锁) 18955 (#2) 关闭 t_LOCK.txt。 --- 这是 18954 (#1) 这是 18956 (#3) 这是 18955 (#2)

(注意独立进程正在争夺STDOUT

【讨论】:

  • NFS 也是flock 存在的祸根。
  • @mob 叹息......我在这里工作(在)NFS :(。当然有一些好处(但这些对我来说并不经常发生)
  • @AndyH 添加了锁定文件访问的具体示例
  • @zdim 谢谢!您的解决方案不提供独占/共享锁功能,但我仍然可以将其用作临时修复,然后根据您的代码创建读写锁。
  • @mpersico 如果你的意思是为什么我没有在这里将它作为一个模块展示,它是为了让它更简单。如果您提到 CPAN ...首先,谢谢您的好话。一方面,我不确定它的普遍用途(至少,要做到这一点需要更多的工作);此外,准备将其发布到那里需要工作。但如果这被认为有用,我会很乐意这样做!
猜你喜欢
  • 2011-01-25
  • 2011-12-04
  • 2010-09-19
  • 2016-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多