我写了一个CGI脚本。其中一个子程序无法同时执行。即同一用户(或黑客)在同一时间运行两个实例)。我怎么能避免这个?
我使用锁定文件进行了以下操作,但我不确定它是否安全:
unless (-e $filelock) {
sub_that_should_be_locked();
}
sub sub_that_should_be_locked {
open FILE, ">", $filelock;
flock DATAFILE, LOCK_EX;
close FILE;
...Code that cannot be executed at the same time...
...Code that cannot be executed at the same time...
unlink $filelock;
}
应该没有等待/队列,并发进程永远不应该调用sub_that_should_be_locked
答案 0 :(得分:4)
这不安全。您的代码中存在争用条件,因为-e
和open
之间存在时间。不要将-e
与锁定文件一起使用。
检查文件是否已锁定,而不是检查文件是否存在。这是使用非阻塞flock
完成的。如果文件尚未锁定,它将返回成功,如果文件已被锁定,它将返回错误EWOULDBLOCK
。
请注意,要使其正常工作,您必须在执行sub_that_should_be_locked
的整个时间内保持锁定。 (您的代码在获得后立即将其释放。)
use Fcntl qw( LOCK_EX LOCK_NB );
sub get_lock_nb {
my ($qfn) = @_;
open(my $fh, '+>:raw', $qfn)
or die("Unable to open file \"$qfn\": $!\n");
if (!flock($fh, LOCK_EX | LOCK_NB)) {
return undef if $!{EWOULDBLOCK};
die("Unable to lock file \"$qfn\": $!\n");
}
return $fh;
}
sub sub_that_should_be_locked {
... Mutually exclusive code ...
}
{
my $lock = get_lock_nb("file.lock");
sub_that_should_be_locked() if $lock;
}
答案 1 :(得分:1)
要同步的线程不应创建和删除锁定文件。
在线程启动之前,您应确保文件存在,然后子例程应使用词法文件句柄打开输入的锁定文件
喜欢这个
open my $lock_fh, '<', $lockfile or die $!;
flock $lock_fh, LOCK_EX;
作为其第一个动作。这将挂起线程,直到它在队列中为止,之后文件将在子例程结束时隐式关闭(因此释放其锁定),因为$lock_fh
超出范围