2

这三个版本是等价的吗?

#!/usr/bin/env perl
use warnings;
use strict;
use 5.10.0;
use Fcntl qw(:flock :seek);
my $fh;

sysopen $fh, $file, O_WRONLY | O_CREAT | O_TRUNC or die $!;
flock $fh, LOCK_EX or die $!;
say $fh 'something';
close $fh;

sysopen $fh, $file, O_WRONLY | O_CREAT or die $!;
flock $fh, LOCK_EX or die $!;
seek $fh, 0, SEEK_SET or die $!;
truncate $fh, 0 or die $!;
say $fh 'something';
close $fh;

sysopen $fh, $file, O_WRONLY | O_CREAT or die $!;
flock $fh, LOCK_EX or die $!;
truncate $fh, 0 or die $!;
say $fh 'something';
close $fh;
4

1 回答 1

2

似乎您想打开一个文件,锁定它,然后截断它,而另一个竞争该锁的进程可能会看到一个截断的文件。这带来了几个限制:

  • 必须在截断之前获得锁。这排除了第一种解决方案,因为它会在获取锁之前进行截断。
  • 文件句柄不能用其他模式重新打开,否则锁将丢失。这意味着不能通过重新打开来完成截断。

因此,理想的解决方案具有以下步骤:

  1. 文件在不截断的情况下打开,例如使用 sysopen 或打开>>模式+<. 此处不得使用sysopen 选项O_TRUNC或打开模式>+>
  2. 获得锁。
  3. 执行截断。与open创建新文件句柄一样,此处必须truncate使用该函数。
  4. 如果文件是以追加模式打开的,则必须seek在文件的开头。

您的第一个解决方案在获取锁之前会截断,因此可以排除。

您的第二个和第三个解决方案都是可能的,尽管这seek是不必要的。

其他解决方案可以使用常规open

open my $fh, "+<", $file;  # Achtung: fails if the file doesn't exist
flock $fh, LOCK_EX;
truncate $fh;

# or:
open my $fh, ">>", $file;
flock $fh, LOCK_EX;
truncate $fh;
seek 0, 0;

在许多情况下,最好使用不相关的文件作为锁,从而避免重新打开时遇到的困难。这看起来像:

# adapted from perldoc -f flock

sub lock {
  my $file = shift;
  open my $fh, "<", ".$file.lock";
  flock $fh, LOCK_EX;
  return $fh;
}

sub unlock {
  my $fh = shift;
  flock $fh, LOCK_UN;
}

my $lock = lock($file);
open my $fh, ">", $file;
...
unlock($lock);

当然,所有相关的进程都必须尊重这个接口并创建一个适当的锁文件。

于 2013-09-15T15:49:15.620 回答