Это зависит от того, должно ли быть 2 часа с момента начала последнего запуска подпрограммы или с момента окончания последнего выполнения.
1) Если последнее (2 часа между окончанием выполнения последней подпрограммы и началом новой), решение cespinoza является совершенно приемлемым (бесконечный цикл и вызовите sleep(7200);
после выполнения подпрограммы).
my $timeout = 7200;
while (1) {
dostuff();
sleep($timeout);
};
Единственная проблема с этим заключается в том, что он не может справиться со случаем, когда dostuff()
занимает вечность, например застрять - для обсуждения, почему это важная ситуация для рассмотрения и подходы к решению, см. ниже.
2) Если первое (2 часа между начальными точками), у вас есть три варианта, связанных с обработкой времени выполнения подпрограммы, которое превышает 2 часа [0] . Ваши 3 варианта, подробно объясненные ниже, являются либо:
2a) запустить новую подпрограмму, пока старая продолжает работать (параллельно);
2b) запустить новую подпрограмму ПОСЛЕ окончания старой;
2c) запустить новую подпрограмму, но сначала остановить выполнение предыдущей.
2a и опции 2c требуют, чтобы вы установили alarm()
на 2 часа и различались тем, что происходит при срабатывании тревоги.
[0] ПРИМЕЧАНИЕ: поскольку любая подпрограмма может потребовать по крайней мере НЕКОТОРЫХ ресурсов с ПК, всегда есть - хотя и небольшая - вероятность того, что она превысит 2 часа, поэтому вам нужно выбрать один из этих трех вариантов обработки такого сценария.
2a) Начинайте каждые 2 часа, параллельно, если не завершено, старое выполнение.
Эта опция, по сути, реализует функциональность cron
.
Каждый раз, когда вы слышите слово параллельно, вы, вероятно, отключите процесс.
my $timeout = 7200;
while (1) { # Not tested!
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
if (!defined($child_pid = fork())) {
die "cannot fork: $!\n";
} elsif (!$child_pid) { # Child
dostuff();
exit;
} # Parent continues to sleep for 2 hours
alarm $timeout; # You need it in case forking off take >2hrs
sleep; # forever
};
die unless $@ eq "alarm\n"; # propagate unexpected errors
# We don't need to check if $@ is true due to forever sleep
}
2b) Начинайте каждые 2 часа, если старый не закончил, дайте ему поработать до конца
Это можно перефразировать как «стартовое задание, если оно завершается быстрее, чем через 2 часа, оставайтесь на ночь»
my $timeout = 7200;
while (1) {
my $start = time;
dostuff();
my $end = time;
my $lasted = $end - $start;
if ($lasted < $timeout) {
sleep($timeout - $lasted);
}
};
2c) Начинайте каждые два часа, если предыдущий не закончился, тайм-аут и убейте его
Всякий раз, когда вы видите такую логику, тревога, очевидно, является ответом.
while (1) {
my $finished = 0;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
alarm 7200;
dostuff();
$finished = 1;
sleep; # forever
};
die unless $@ eq "alarm\n"; # propagate unexpected errors
warn "Timed out!!!\n" unless $finished
}
P.S. Как отметил cespinoza, вам нужно каким-то образом демонизировать скрипт (убедитесь, что он не будет убит при выходе из оболочки, запустившей его), с помощью средств Unix (например, запуск его как nohup) или Perlish (поиск daemonize + Perl на Stackoverflow для механики этого).