Запуск только одного экземпляра Perl-скрипта cron - PullRequest
20 голосов
/ 10 февраля 2010

Мне нужно периодически запускать Perl-скрипт cron (~ каждые 3-5 минут). Я хочу убедиться, что за один раз будет запущен только один экземпляр сценария Perl, поэтому следующий цикл не начнется, пока не закончится предыдущий. Может ли / должно ли это быть достигнуто с помощью некоторых встроенных функций cron, Perl или мне нужно обрабатывать это на уровне сценария?

Я довольно новичок в Perl и cron, поэтому помощь и общие рекомендации приветствуются.

Ответы [ 7 ]

14 голосов
/ 10 февраля 2010

Мне всегда везло, используя File :: NFSLock , чтобы получить эксклюзивную блокировку самого скрипта.

use Fcntl qw(LOCK_EX LOCK_NB);
use File::NFSLock;

# Try to get an exclusive lock on myself.
my $lock = File::NFSLock->new($0, LOCK_EX|LOCK_NB);
die "$0 is already running!\n" unless $lock;

Это то же самое, что и другие предложения файла блокировки, за исключением того, что мне не нужно ничего делать, кроме попытки получить блокировку.

11 голосов
/ 13 февраля 2012

Модуль Sys :: RunAlone делает то, что вы хотите, очень приятно. Просто добавьте

  use Sys::RunAlone;

в верхней части вашего кода.

11 голосов
/ 10 февраля 2010

Используйте File :: Pid , чтобы сохранить pid скрипта в файле, который скрипт должен проверить в начале, и прервать, если он найден. Вы можете удалить pidfile, когда скрипт будет выполнен, но в этом нет особой необходимости, так как вы можете просто проверить позже, чтобы увидеть, жив ли этот идентификатор процесса (что также будет учитывать случаи, когда ваш скрипт неожиданно прерывается):

use strict;
use warnings;
use File::Pid;

my $pidfile = File::Pid->new({file => /var/run/myscript});
exit if $pidfile->running();

$pidfile->write();

# ... rest of script...

# end of script
$pidfile->remove();
exit;
3 голосов
/ 10 февраля 2010

Типичный подход для каждого процесса - открыть и заблокировать определенный файл. Затем процесс считывает идентификатор процесса, содержащийся в файле.

Если процесс с таким идентификатором запущен, опоздавший спокойно завершает работу. В противном случае новый победитель записывает свой идентификатор процесса ($$ в Perl) в pid-файл, закрывает дескриптор (который снимает блокировку) и продолжает свое дело.

Пример реализации ниже:

#! /usr/bin/perl

use warnings;
use strict;

use Fcntl qw/ :DEFAULT :flock :seek /;

my $PIDFILE = "/tmp/my-program.pid";
sub take_lock {
  sysopen my $fh, $PIDFILE, O_RDWR | O_CREAT or die "$0: open $PIDFILE: $!";
  flock $fh => LOCK_EX                       or die "$0: flock $PIDFILE: $!";

  my $pid = <$fh>;
  if (defined $pid) {
    chomp $pid;
    if (kill 0 => $pid) {
      close $fh;
      exit 1;
    }
  }
  else {
    die "$0: readline $PIDFILE: $!" if $!;
  }

  sysseek  $fh, 0, SEEK_SET or die "$0: sysseek $PIDFILE: $!";
  truncate $fh, 0           or die "$0: truncate $PIDFILE: $!";
  print    $fh "$$\n"       or die "$0: print $PIDFILE: $!";
  close    $fh              or die "$0: close: $!";
}

take_lock;
print "$0: [$$] running...\n";
sleep 2;
1 голос
/ 28 июля 2016

Я всегда использовал это - маленький и простой - не зависит ни от какого модуля и работает как Windows + Linux.

use Fcntl ':flock';                    

### Check to make sure there is only one instance ###
open SELF, "< $0" or die("Cannot run two instances of this program");
unless ( flock SELF, LOCK_EX | LOCK_NB ) {
    print "You cannot run two instances of this program , a process is still running";
    exit 1;
}
1 голос
/ 10 февраля 2010

AFAIK Perl не имеет такой встроенной функции.Вы можете легко создать временный файл, когда вы запустите свое приложение и удалите его, когда ваш скрипт будет готов.

0 голосов
/ 10 февраля 2010

Учитывая частоту, я обычно пишу демон (сервер), который приятно ждет без дела между запусками заданий (т. Е. sleep()), вместо того, чтобы пытаться использовать cron для достаточно детального доступа.

Если необходимо, в системах Unix / Linux вы можете запустить его с /etc/inittab (или с заменой), чтобы гарантировать, что он всегда работает и автоматически перезапускается в процессе, уничтожается или умирает.

Добавлено : (и некоторые ненужные вещи удалены)

Преимущество всегда присутствующего (работающего, но в основном бездействующего) демона заключается в исключении возможности одновременного запуска сценария cron автоматически.

Однако это означает, что вы несете ответственность за правильное управление временем, например, в случае наложения (т. Е. Предыдущий прогон все еще выполняется, в то время как происходит новый триггер ). Это может помочь вам решить, использовать ли разветвляющийся демон или не разветвленный дизайн. Потоки не дают никаких преимуществ в этом сценарии, поэтому нет необходимости рассматривать их использование.

Это не полностью исключает возможность запуска нескольких процессов, но является общей проблемой для многих демонов. Типичное решение - использовать семафор, такой как взаимоисключающая блокировка файла, для предотвращения запуска второго экземпляра. При завершении процесса блокировка файла автоматически забывается, поэтому в случае аварийного завершения (например, сбоя питания) не требуется очистка самой блокировки.

Подход с использованием модуля Fcntl и использованием Perl sysopen с флагом O_EXCL (или O_RDWR | O_CREAT | O_EXCL) был , данный Грегом Бэконом . Единственные различия, которые я хотел бы сделать, - это объединить эксклюзивную блокировку в вызове sysopen (т.е. использовать предложенные мной флаги) и удалить тогда избыточный вызов flock. О, и я бы следовал соглашениям о файловой системе UNIX (& Linux FHS) и именовании /var/run/daemonname.pid.

Другим подходом было бы использовать daemontools или , аналогичные , для "демонизации" задачи.

...