Но что вы будете делать, пока у кого-то еще есть замок? Выйти и попробовать позже? Busy-ждать?
Если вам не нужна синхронизация, хорошая ставка - sysopen
с установленными флагами O_EXCL
и O_CREAT
, что создаст файл, только если он не существует.
use Fcntl qw/ :DEFAULT /;
# ...
sysopen my $fh, $LOCKFILE, O_EXCL|O_CREAT
or die "$0: sysopen: $!";
Но обратите внимание на следующее предостережение со страницы руководства Linux open(2)
:
O_EXCL
поддерживается только в NFS при использовании NFSv3 или новее в ядре 2.6 или новее. В средах, где поддержка NFS O_EXCL
не предоставляется, программы, которые используют ее для выполнения задач блокировки, будут содержать условие состязания. Переносимые программы, которые хотят выполнять атомарную блокировку файлов с помощью файла блокировки и должны избегать использования поддержки NFS для O_EXCL
, могут создавать уникальный файл в той же файловой системе (, например, , включая имя хоста и PID) и используйте link(2)
, чтобы сделать ссылку на файл блокировки. Если link(2)
возвращает 0, блокировка успешна. В противном случае используйте stat(2)
для уникального файла, чтобы проверить, увеличилось ли количество ссылок до 2, и в этом случае блокировка также будет успешной.
«Я бы предпочел иметь сетевую файловую систему, а не NFS», как говорится, поэтому держите ваши координирующие процессы на одной машине, если можете.
Вы можете использовать flock
, как показано в коде ниже:
#! /usr/bin/perl
use warnings;
use strict;
use Fcntl qw/ :DEFAULT :flock /;
my $LOCKFILE = "/tmp/mylock";
sub acquire_lock {
sysopen my $fh, $LOCKFILE, O_RDWR|O_CREAT or die "$0: open: $!";
flock $fh, LOCK_EX or die "$0: flock: $!";
$fh;
}
sub work {
for (1 .. 2) {
my $fh = acquire_lock;
print "$0: $$ has lock\n";
sleep rand 3;
close $fh or warn "$0: [$$] close: $!";
}
exit;
}
Для демонстрации приведенный ниже код разветвляет пятерых детей, которые по очереди приобретают замок:
my $KIDS = 5;
my %pids;
for (1 .. $KIDS) {
my $pid = fork;
die "$0: fork: $!" unless defined $pid;
$pid ? ++$pids{$pid} : work;
}
while (my $pid = wait) {
last if $pid == -1;
warn "$0: unknown child $pid" unless delete $pids{$pid};
}
warn "$0: still alive: " .
join(", " => sort { $a <=> $b } keys %pids) .
"\n"
if keys %pids;
Пример вывода:
./kidlock: 26644 has lock
./kidlock: 26645 has lock
./kidlock: 26646 has lock
./kidlock: 26645 has lock
./kidlock: 26648 has lock
./kidlock: 26646 has lock
./kidlock: 26647 has lock
./kidlock: 26647 has lock
./kidlock: 26644 has lock
./kidlock: 26648 has lock