Perl 中的读写锁 [英] Read-write lock in Perl

查看:35
本文介绍了Perl 中的读写锁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

谢谢!

解决方案

flock 不会跨线程做你想做的事.

您可以使用 sysopen 实现自己的锁定,如果文件存在,则锁定失败与 O_EXCL|O_CREAT 一起使用.

一个例子,子进程争夺锁

使用警告;使用严格;使用功能说";使用 Fcntl;使用 Time::HiRes qw(sleep);我的 $lock_file = ".lock.$$";子 get_lock {我的 ($file, $pid) = @_;我的 $fh;而(不是 sysopen $fh、$file、O_WRONLY|O_EXCL|O_CREAT){说 "\t($$: 锁文件存在 ..)";睡眠 0.5;}说 $fh $pid;}子 release_lock {我的 ($file, $pid) = @_;unlink $file or die "Error unlike $file: $!";说 "\t($$: 释放锁)";}我的@pids;对于 (1..4) {my $pid = fork//死不能分叉:$!";如果($pid == 0){睡眠兰特 1;get_lock($lock_file, $$);说$$,锁定和处理";睡眠兰特 1;release_lock($lock_file, $$);说$$已完成.";出口}推@pids, $pid;}等待@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 {我的 ($file, $mode) = @_;get_lock($lock_file, $$);打开我的 $fh, $mode, $file 或 die "Can't open $file: $!";返回 $fh;}子 close_and_release {我的 ($fh) = @_;关闭 $fh;release_lock($lock_file, $$);返回 1;}

这些可以与 get_lockrelease_lock 一起放在一个模块中,例如,锁文件名作为包全局.

一个简单的测试驱动

# 使用上面的语句使用 Path::Tiny;# 只显示文件我的 $lock_file = ".lock.file.access.$$";我的 $file = 't_LOCK.txt';我的@pids;对于 (1..4){my $pid = fork//死不能分叉:$!";如果($pid == 0){睡眠兰特 1;我的 $fh = open_with_lock($file, '>>');说 "$$ (#$_) 打开 $file ..";说 $fh "这是 $$ (#$_)";睡眠兰特 1;close_and_release($fh);说 "$$ (#$_) 关闭了 $file.";说 ' - -';出口;}推@pids, $pid;}等待@pids;打印路径($file)->slurp;取消链接 $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)

I am looking for a good way to implement read/write lock in Perl. This is needed to synchronize file access from different Perl threads and/or processes on Windows and Unix. Tried Fcntl::flock which would be perfect for me if it worked as expected. Unfortunately it looks like under stress flock allows to set lock on already locked file in another thread. Looked into some CPAN modules, but most are implemented with flock. Next I am planning to evaluate fcntl for Unix and Win32::Mutex for Windows. This seems like a pretty common task and perhaps I am missing some simple solution. If you are aware of any, could you please point it out for me?

Thank you!

解决方案

The flock won't do what you want across threads.

You can implement your own locking using sysopen, which fails if the file exists when used with O_EXCL|O_CREAT.

An example, with child processes competing for a lock

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;

It is better use File::Temp for the lockfile name but read the docs carefully for subtleties.

Sample output with 3 processes

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.


The O_EXCL may be unsuppored under NFS: you must have at least 2.6 kernel and NFSv3 or there will be a race condition. If this is a problem the workaround is to use link(2) to obtain a lock. See man 2 open (also for other details since sysopen uses open syscall).


To lock only file access, for example

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

These can be placed in a module along with get_lock and release_lock, with the lock-file name as a package global, for example.

A simple test-driver

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

With use statements from the first example and with 3 forks, a run

        (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)

(note that independent processes are fighting for STDOUT)

这篇关于Perl 中的读写锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆