【问题标题】:Run a sub in a perl script based on the time?根据时间在 perl 脚本中运行子程序?
【发布时间】:2011-08-26 21:28:50
【问题描述】:

我有一个 perl 脚本,它一直作为守护进程循环运行。我想在基于时间(或计时器)的 perl 脚本中运行一个子函数,因此它每 2 小时运行一次该子函数并继续它的循环。我正在考虑获取纪元时间并通过循环检查几次,一旦它大于 2 小时,它就会运行子函数。在 perl 中有没有更好的方法来做到这一点?

谢谢,
LF4

【问题讨论】:

  • 有什么理由不将该功能移至单独的脚本并从 cron 调用它?
  • 脚本在登录后使用 IMAP 检查电子邮件,它以给定的速率检查新邮件,从不退出。我遇到了它会挂起的问题,我认为这是因为会话将过期。我想将脚本设置为在给定的时间范围内注销和登录,这样它就不会挂起但不会中断检查循环。
  • 另一种方法是检查每个 IMAP 事务后生成的任何错误,看看它们是否表明连接已被切断。如果是,请重新初始化连接并重新发出交易。

标签: perl


【解决方案1】:

这取决于自上次子例程启动的 START 或自上次执行的 END 以来是否应该有 2 小时。


1) 如果是后者(在运行最后一个子程序结束和开始新子程序之间的 2 小时),cespinoza 的解决方案是完全可以接受的(无限循环,并在执行子程序后调用sleep(7200);)。

my $timeout = 7200;
while (1) {
    dostuff();
    sleep($timeout);
};

唯一的问题是它无法处理dostuff() 永远占用的情况,例如卡住了 - 关于为什么这是一个需要考虑的重要情况和解决方法的讨论,请参见下文。


2) 如果是前者(起点之间的时间为 2 小时),您有 3 个选项,涉及处理超过 2 小时的子程序运行时间[0]。下面详细解释的 3 个选项是:

2a) 在旧子程序继续运行(并行)的同时启动一个新子程序;

2b) 在旧子程序完成后启动新子程序;

2c) 开始一个新的子程序,但首先停止前一个子程序的执行。

2a 和 2c 选项要求您将 alarm() 设置为 2 小时,并且在触发警报时会发生什么不同。

[0] 注意:由于任何子例程都可能需要至少一些来自 PC 的资源,所以总是有可能超过 2 小时 - 无论多么小 - 机会,所以你必须选择处理这种情况的这 3 个选项之一。


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
}

附:正如 cespinoza 所指出的,您需要通过 Unix 方式(例如,以 nohup 启动它)或 Perlish 方式(搜索 daemonize + Perl on Stackoverflow 的机制)。

【讨论】:

  • 这是一个深思熟虑的答案,但根据我对这个问题的理解,这似乎会在睡眠时阻塞主执行循环。因此它只能每 2 小时 dostuff() 一次。我的理解是它在一个连续循环中调用 dostuff() 可能每小时 100 次,然后每 2 小时它需要 do_subfunction()。
  • @ben - 这很容易融入所有 3 个“2abc”解决方案。在前面加上“睡觉”;在循环中使用“每 N 分钟调用一次”(在基于警报的解决方案的 eval 内)。
  • 感谢 DVK 非常周到的回答,该脚本每 15 秒检查一次收件箱中是否有任何新邮件,并设置为守护进程。
【解决方案2】:

像 crontab 这样的东西最好是做这样的定时工作。但是,如果您想运行 Perl 守护程序,则必须使用某种事件处理程序。我想到的两个选择是POEAnyEvent

【讨论】:

  • 使用这样的偶数处理程序可以让您拥有多个计时器。因此,如果您想每 5 分钟一次 dostuff() 和每 2 小时一次 do_otherstuff() 一次,事件处理程序可以为您解决这个问题。你可以通过检查纪元时间来自己做,但是有相当多的复杂性(如果你在跑步时时间改变了怎么办?)你会重新发明轮子。
【解决方案3】:

您可能需要查看Schedule::Cron 以了解任务计划和执行情况。

【讨论】:

    猜你喜欢
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 2015-05-27
    • 2011-03-13
    • 1970-01-01
    • 2010-09-26
    相关资源
    最近更新 更多