简体   繁体   English

以精确的时间运行子程序,perl精度为0.1s

[英]Run a subroutine at exact times with 0.1s precision in perl

Need run a subroutine every 5 seconds, but measured at system clock marks. 需要每5秒运行一个子程序,但需要在系统时钟标记处测量。 So, need start it every minute at 0, 5, 10, 15.... 45, 50, 55 second (exactly, with 0.1sec precistion). 所以,需要在0,5,10,15 .... 45%,5055秒启动它每分钟(确切地,与0.1秒precistion)。

Something like: 就像是:

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

The run_this sub can be fast or slow (its runtime between 0.2 - 120 seconds). run_this子可以快或慢(其运行时间在0.2 - 120秒之间)。 When it run more than 5 seconds - regardless its runtime, the next run must be at exact 5 second mark. 当它运行超过5秒时 - 无论其运行时间如何,下一次运行必须精确到5秒。

Eg when the run_this 例如,当run_this

  • ends at 11:11:12.3 need wait 2.7 second to the next run at 11:11:15 结束于11:11:12.3需要等待2.7秒到下一次运行11:11:15
  • when ends at 11:11:59.2 need wait only 0.8 seconds to the next at 11:12:00, and so on... 当以11:11:59.2结束时,需要在11:12:00等待0.8秒到下一个,依此类推......

The question is: how to write the do_sleep? 问题是:如何编写do_sleep?

For 0.1s precision you need to use Time::HiRes module. 对于0.1s精度,您需要使用Time :: HiRes模块。 Something like: 就像是:

#!/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)
}

This one also works if you have signal handlers. 如果您有信号处理程序,这个也可以工作。 It also handles leap seconds. 它还处理闰秒。

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());

Note that the system might not give a time slice when you want it, so you might actually start later than requested. 请注意,系统可能不会在您需要时提供时间片,因此您实际上可能会比请求时间晚开始。

A very different method would be to use IO::Async for this. 一种非常不同的方法是使用IO :: Async You can schedule an event for a specific time in the future. 您可以在将来的某个特定时间安排活动。

Use a high precision timer from Time::HiRes to time the loop 使用Time :: HiRes中的高精度计时器来计时循环

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

Put your long running job into a background process 将您的长期工作纳入后台流程

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

See also Initiating Non-waiting Background Process in Perl 另请参阅在Perl中启动非等待后台进程

You could use Time::HiRes and figure how long to wait that way: 您可以使用Time :: HiRes并计算等待多长时间:

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

I did not test the code and there may be some boundary condition when the call finishes at exactly 5 seconds boundary. 我没有测试代码,当调用在5秒的边界完成时可能会有一些边界条件。 But I think it gives the right idea. 但我认为它给出了正确的想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM