2012-07-30 226 views
3

需要每5秒鐘運行一個子程序,但在系統時鐘標記處測量。所以,需要開始每分鐘0,5,10,15 .... 45,50,55秒(精確地說,精確到0.1秒)。在精確的時間運行子程序,精度爲0.1s,精度爲perl

是這樣的:

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

run_this子可快可慢(其0.2之間運行時 - 120秒)。當運行時間超過5秒 - 無論運行時間如何,下一次運行必須精確到5秒。

E.g.當run_this

  • 結束於11:11:12.3需要在11點11分十五秒
  • 等待2.7秒下一個運行時在端部11:11:59.2需要等待只有0.8秒至下一個在11:12:00,依此類推...

問題是:如何編寫do_sleep?

+0

如果'run_this()'可能需要超過5秒鐘,那麼沒有辦法保證'run_this()'將每5秒運行一次。不是在一個線程中,反正......如果'run_this()'是線程安全的,那麼你可以讓一個線程每五秒鐘啓動一個'run_this()'。 – 2012-07-30 19:51:39

+0

我的意思是在分組結束後5秒鐘不睡覺。所以當運行時間爲53.2秒時,睡眠時間爲1.8秒,接下來的5秒標記。 – kobame 2012-07-30 19:57:19

+0

啊,好的。在那種情況下,jm666或者Jim Corbett的答案都應該有效。 – 2012-07-30 19:59:36

回答

5

對於0.1s精度,您需要使用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) 
} 
+0

我希望比22:05開始的時候: 35.2341的意思是0.0002341秒。 ;)令人討厭的mili-micro .. :)謝謝。接受。 – kobame 2012-07-30 20:07:20

+0

@ andras-fekete請發表您自己的回答。 – jm666 2016-03-18 23:06:52

1

您可以使用Time :: HiRes並計算等待時間:

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

我沒有測試代碼,並且可能會有一些邊界條件,當調用在完全5秒的邊界完成時。但我認爲它提供了正確的想法。

+0

它就像上面,所以謝謝你) – kobame 2012-07-30 20:04:56

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

請注意,系統可能不會在您需要時提供時間片,因此您可能實際上比要求的時間晚。

+0

謝謝,也會試一試。 ;) – kobame 2012-07-30 20:51:48

+0

@ kobame,經過測試。修復了一些小錯誤。 – ikegami 2012-07-30 20:57:21

+0

@kobame,使用DateTime的更輕的替代方案。 – ikegami 2012-07-30 21:05:05

2

一個非常不同的方法是使用IO::Async。您可以安排將來某個特定時間的活動。

+0

具體而言,您需要一個間隔5秒的'IO :: Async :: Timer :: Periodic'實例。 – LeoNerd 2012-08-15 10:36:35