首页 > 解决方案 > Perl:以独占方式打开文件的最佳方式?

问题描述

我知道 Perl 的 flock() 函数。我遇到的问题是它需要先打开文件才能锁定它(IIUC)。让我解释一下我的问题。

我有一个文件可以由许多用户同时更新。用户可以读取该文件,进行一些小的更改,然后再次将其写回磁盘。

假设用户 A 需要更新文件。它打开它(使用open()),然后尝试flock() 它。如果用户 B 的文件已经打开并锁定,用户 A 的flock() 将阻塞。一旦用户 B 将更改的文件写入磁盘并解锁,用户 A 的flock() 就会解除阻塞。但是此时,用户 A 打开的文件版本比用户 B 已写入(写入磁盘)的版本旧。因此,如果用户 A 然后对文件进行更改并将其写入磁盘,则用户 B 的更改丢失。或者至少,这就是我认为flock() 的工作方式。

为了防止这种情况发生,我(在 Perl 中)编写了 LockFile() 和 UnlockFile() 函数。它的作用是,当需要以独占方式打开文件时,它首先为该特定文件创建一个锁定文件(如果它不存在)并锁定它。如果锁定成功,我就可以打开我想要独占使用的真实文件。当我处理完真正的文件后,我将它写入磁盘,然后解锁锁定文件。

在代码中:

BEGIN {
  our $DIR_Locks;
  my %filelocks;
  my $lockdir = $DIR_Locks;

  #############################
  sub LockFile {
    my ($FilePath, $blocking) = @_;

    my $success;

    $blocking = 1 if( !defined( $blocking ) );
    my $LockFile = _getLockFileName( $FilePath );
    my $fh = IO::File->new();

    if( ! -f $LockFile ) {
      open( $fh, ">$LockFile" );
      close( $fh );
    }
    if( ! open( $fh, "$LockFile" ) ) {
      return 0;
    } else {
      chmod( 0777, *$fh );
      if( $blocking ) {
        $success = flock( $fh, 2 );
      } else {
        $success = flock( $fh, 6 );
      }
      $filelocks{$LockFile} = $fh if( $success );
      return $success;
    }
  }

  #############################
  sub UnlockFile {
    my $FilePath = shift;
    my $LockFile = _getLockFileName( $FilePath );
    if( $filelocks{$LockFile} ) {
      flock( $filelocks{$LockFile}, 8 );
      close( $filelocks{$LockFile} );
    }
  }

  #############################
  sub _getLockFileName {
    my $FilePath = shift;
    my $LockFile;
    my ($dir, $file) = ($FilePath =~ /(.*)\/(.*)/s);
    if( $file eq "" && $dir eq "" ) {
      $file = $FilePath;
    }
    $LockFile = "$lockdir/$file.lock";
    return $LockFile;
  }

}

但是......它似乎不起作用......我的文件仍然不时损坏。并且以这样一种方式,看起来有人正在打开文件,同时其他人正在将其写入磁盘。我怀疑这是因为磁盘写入可能已被缓存,这意味着 Perl 认为写入已完成,然后解锁锁定文件并且其他人打开该文件,而实际上该文件正在写入磁盘(由缓存处理程序)。

我的问题是:

我对flock() 工作原理的理解是否正确?或者我是否真的通过锁定另一个文件而不是我想要独占使用的实际文件来创建问题?

编辑 我应该添加一个我如何使用它的示例。对不起。:)

LockFile( "somefile.dat" );

open( FILE, "somefile.dat" );
my $contents = SlurpFile( "somefile.dat" );
# SlurpFile() above reads the entire file at once
close( FILE );

# Do some stuff with $contents here

open( FILE, ">somefile.dat" );
print FILE $contents;
close( FILE );

UnlockFile( "somefile.dat" );

现在 LockFile() 实际上并没有锁定“somefile.dat”,而是锁定了“somefile.dat.lock”(如果它不存在则在进程中创建它)。它只是阻止其他用户(运行相同的脚本)打开和更改“somefile.dat”的内容,而其内容仍在处理中并且尚未写回磁盘。

但是我有时会看到 somefile.dat 突然丢失了大量数据,就好像有人在文件仍在写入磁盘的过程中打开了文件一样。所以一个新用户实际上只读取了文件的一部分(从一开始)......

标签: fileperllocking

解决方案


推荐阅读