【问题标题】:Run a subroutine at exact times with 0.1s precision in perl在 perl 中以 0.1 秒的精度在准确的时间运行子程序
【发布时间】:2012-07-30 19:47:50
【问题描述】:

需要每 5 秒运行一次子程序,但在系统时钟标记处测量。因此,需要在每分钟 0、5、10、15.... 45、50、55 秒时开始它(确切地说,以 0.1 秒的精度)。

类似:

for(;;) {
    do_sleep(); #time need to sleep to the next 5 second mark
    run_this();
}

run_this sub 可以快也可以慢(它的运行时间在 0.2 - 120 秒之间)。当它运行超过 5 秒时 - 无论其运行时间如何,下一次运行都必须精确到 5 秒。

例如当run_this

  • 在 11:11:12.3 结束需要等待 2.7 秒才能在 11:11:15 进行下一次运行
  • 在 11:11:59.2 结束时只需等待 0.8 秒即可到下一个 11:12:00,依此类推...

问题是:do_sleep怎么写?

【问题讨论】:

  • 如果run_this() 的运行时间超过五秒,则无法保证run_this() 每五秒运行一次。无论如何,不​​在单个线程中...如果run_this() 是线程安全的,那么您可以让一个线程每五秒触发一次run_this()
  • 我的意思是在潜艇结束后 5 秒内不要睡觉。因此,当运行时间为 53.2 秒时,休眠 1.8 秒到下一个 5 秒标记。
  • 啊,好吧。在这种情况下,jm666 或 Jim Corbett 的答案都应该有效。
  • 你在做什么需要这么精确?可能有更好的方式来协调工作。
  • run_this 通过 USB 和微控制器做一些事情,细节只有我们的硬件人员知道.. ;(

标签: perl


【解决方案1】:

对于 0.1 秒的精度,您需要使用 Time::HiRes 模块。比如:

#!/usr/bin/perl
use 5.014;
use warnings;
use Time::HiRes qw(tv_interval usleep gettimeofday);

for(;;) {
    do_sleep();
    run_this();
}

sub do_sleep {
    my $t = [gettimeofday];
    my $next = (int($t->[0]/5) + 1) * 5;
    my $delta = tv_interval ($t, [$next, 0]);
    usleep($delta * 1_000_000);
    return;
}

sub run_this {
    my $t = [gettimeofday];
    printf "Start is at: %s.%s\n",
        scalar localtime  $t->[0],
        $t->[1];
    usleep( rand 10_000_000 );  #simulating the runtime between 0-10 seconds (in microseconds)
}

【讨论】:

  • 我希望像 22:05:35.2341 这样的开始时间意味着 0.0002341 秒。 ;) 讨厌的 mili-micro.. :) 谢谢。接受。
  • @andras-fekete 请发表您自己的答案。
【解决方案2】:

如果您有信号处理程序,这个也可以工作。它还处理闰秒。

use Time::HiRes qw( );

sub uninterruptible_sleep_until {
   my ($until) = @_;
   for (;;) {
      my $length = $until - Time::HiRes::time();
      last if $length <= 0;
      Time::HiRes::sleep($length);
   }
}

sub find_next_start {
   my $time = int(time());
   for (;;) {
      ++$time;
      my $secs = (localtime($time))[0];
      last if $secs % 5 == 0 && $secs != 60;
   }
   return $time;
}

uninterruptible_sleep_until(find_next_start());

请注意,系统可能不会在您需要时提供时间片,因此您实际上可能会比请求的时间晚。

【讨论】:

  • 谢谢,我也试试。 ;)
  • @kobame,经过测试。修复了一些小错误。
  • @kobame,使用了较轻的 DateTime 替代品。
【解决方案3】:

一个非常不同的方法是为此使用IO::Async。您可以在未来的特定时间安排活动。

【讨论】:

  • 具体来说,您需要一个间隔为 5 秒的 IO::Async::Timer::Periodic 实例。
【解决方案4】:

使用 Time::HiRes 中的高精度计时器为循环计时

http://perldoc.perl.org/Time/HiRes.html

将长期运行的作业放入后台进程

my $pid = fork;
die "fork failed" unless defined $pid;
if ($pid == 0) {
    # child process goes here
    run_this();
    exit;
}
# parent process continues here

另请参阅 Initiating Non-waiting Background Process in Perl

【讨论】:

    【解决方案5】:

    您可以使用 Time::HiRes 并计算以这种方式等待多长时间:

    use Time::HiRes;
    my $t = time();
    my $nextCallTime = int($t) / 5 * 5 + 5;
    my $timeToWait = $nextCallTime - $t;
    sleep($timeToWait);
    

    我没有测试代码,当调用恰好在 5 秒边界结束时可能存在一些边界条件。但我认为它给出了正确的想法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-26
      相关资源
      最近更新 更多