当睡眠()不能很好地报警时,我还可以做什么'睡眠'?

时间:2011-07-28 08:04:38

标签: perl sleep alarm

有很多文件说“你应该避免使用带闹钟的睡眠,因为很多系统都会使用闹钟来实现睡眠”。实际上,我遇到了这个问题。 那么,当睡眠()无法正常使用警报时,是否有人可以帮助我,我可以做什么'睡眠'?我已经尝试了'usleep'的Time :: HiRes模块和select()函数。但它们也没有用。

6 个答案:

答案 0 :(得分:4)

看到你被闹钟打断了,因此无法可靠地使用sleep()或select(),我建议将Time :: HiRes :: gettimeofday与select()结合使用。

这是我没有测试过的一些代码。它应该抵抗被信号中断,并且将睡眠所需的秒数加上0.1秒。如果你愿意燃烧更多的CPU周期而不做任何工作,你可以使分辨率更好:

...
alarm_resistant_sleep(5); # sleep for 5 seconds, no matter what
...

use Time::HiRes;

sub alarm_resistant_sleep {
  my $end = Time::HiRes::time() + shift();
  for (;;) {
    my $delta = $end - Time::HiRes::time();
    last if $delta <= 0;
    select(undef, undef, undef, $delta);
  }
}

答案 1 :(得分:1)

您可以通过system

在新流程上停留
system ( "sleep", 5 );

或者我误解了这个问题?

答案 2 :(得分:1)

您可以尝试AnyEvent

use AnyEvent;

my $cv = AnyEvent->condvar;
my $wait_one_and_a_half_seconds = AnyEvent->timer(
    after => 1.5,
    cb => sub { $cv->send }
);     
# now wait till our time has come
$cv->recv;

答案 3 :(得分:1)

使用时(来自MySQL forum

use Sys::SigAction qw( set_sig_handler );
eval {
  my $hsig = set_sig_handler( 'ALRM', sub { my $canceled = 1; die; }, { mask=>[ qw( INT ALRM ) ] ,safe => 0 } );
  alarm($timeout);
  ...
  alarm(0);
}

我注意到,对sleep($delay) $timeout短于$delay的所有后续调用最终都会终止脚本执行,并打印“Alarm clock

我找到的解决方法是再次拨打alarm(),但值不大(3600),并立即取消该警报。

eval {
  alarm(3600);
  print " .... Meeeep ....";  # Some trace
  alarm(0); 
};

然后我可以使用sleep()而不再受到干扰。

以下示例(实时代码段):

sub unmesswithsleep {
  eval {
    alarm(3600);
    &tracing (8, " .... Meeeep ....");
    alarm(0); 
  };
}


sub lockDBTables {
  return (0) unless ($isdbMySQLconnect);
  my $stm = qq {
    LOCK TABLES 
      myBIGtable WRITE
  };

  my $timeout = 60;          # This is the timer set to protect against deadlocks. Bail out then.
  eval { 
    my $h = set_sig_handler( 'ALRM', sub { my $canceled = 1; die; }, { mask=>[ qw( INT ALRM ) ] ,safe => 0 } ); 
    alarm($timeout); 
    my $res = $dbmyh->do($stm) + 0;
    alarm(0);  # Reset alarm
  };

  if ( $@ =~ m/Die/i ) {
    $isdbTabledlocked = 0;
    &tracerr (0, "FATAL: Lock on Tables has NOT been acquired within ${timeout}s. Lock is set to <$isdbTabledlocked>.");
    &unmesswithsleep();   # MUST be called each time alarm() is used
    return (0);
  } else { 
    $isdbTabledlocked = 1;
    &tracing (2, "  Good: Lock on Tables has been acquired in time. Lock is set to <$isdbTabledlocked>.");
    &unmesswithsleep();   # MUST be called each time alarm() is used
    return (1);
  }
  # Can use sleep() now.
}

答案 4 :(得分:0)

尝试

print "Start\n";
select undef, undef, undef, 1;
print "End\n";                                                                  

这会睡1秒钟。

答案 5 :(得分:0)

听起来你的代码会被一些设置闹钟的代码打断。这是设计使您看到预期的行为。换句话说,警报*应该总是中断睡眠呼叫。

如果您正在寻找一种纯粹的perl睡眠方式而不会被警报打断,您可以通过安装自己的警报信号处理程序来完成此操作。这样,当您的代码发出警报时,它不会中断您的处理。

但是,一个重要的警告是,这将延迟其他代码设置的任何警报。其他代码将迟到警报;代码完成后这意味着如果你想与他人合作,你最好使用其他解决方案之一。

以下是一个例子:

#!/usr/bin/perl

use POSIX;
use strict;
use warnings;

# set an alarm
print "Setting alarm\n";
alarm 1;

my $old_alarm;
my $snoozed;
{
  # store the previous alarm handler (if any)
  $old_alarm = $SIG{ALRM};

  # override the alarm handler so that we don't
  # get interrupted
  local $SIG{ALRM} = sub {
    print "got alarm; snoozing\n";

    # record the fact that we caught an alarm so that
    # we can propagate it when we're done
    $snoozed++;
  };

  # sleep for a while.
  for (1 .. 3) {
    print "z" x $_ ,"\n";
    sleep 1;
  }
}

# replace the old sleep handler;
$SIG{ALRM} = $old_alarm
 if $old_alarm;

# if we had to snooze fire an immediate alarm;
if ($snoozed) {
  POSIX::raise(POSIX::SIGALRM);
}

您引用的文档提示但未描述不同的症状。当通过警报实现睡眠时,您需要担心的主要问题是当有人呼叫睡眠时重置警报。

*显然有一些版本的perl(例如:旧的Win32),其中一个警报不会中断睡眠。