Что еще я могу сделать «спать», когда sleep () не может хорошо работать с тревогой? - PullRequest
6 голосов
/ 28 июля 2011

Существует много документов, в которых говорится, что «вам следует избегать использования режима сна с тревогой, поскольку многие системы используют сигнализацию для реализации режима сна».И на самом деле, я страдаю от этой проблемы.Так может ли кто-нибудь помочь мне, что еще я могу сделать «спать», когда sleep () не может хорошо работать с тревогой?Я уже пробовал «уснуть» модуля Time :: HiRes и выбрать функцию ().Но они тоже не работали.

Ответы [ 6 ]

4 голосов
/ 28 июля 2011

Поскольку вас прерывают сигналы тревоги, и вы не можете надежно использовать sleep () или select (), я предлагаю использовать Time :: HiRes :: gettimeofday в сочетании с select ().* Вот код, который я не проверял.Он должен противостоять прерыванию сигналами и будет спать в течение необходимого количества секунд плюс до 0,1 секунды.Если вы хотите использовать больше циклов ЦП, не делая ничего продуктивного, вы можете значительно улучшить разрешение:

...
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 голос
/ 18 июля 2013

При использовании (с 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.
}
1 голос
/ 28 июля 2011

Вы можете попробовать 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;
1 голос
/ 28 июля 2011

Вы можете перейти на новый процесс через system:

system ( "sleep", 5 );

Или я неправильно понял вопрос?

0 голосов
/ 01 сентября 2011

Похоже, что ваш код, который спит, прерывается каким-то кодом, который устанавливает будильник.Это сделано так, что вы видите ожидаемое поведение.Другими словами, тревога * всегда должна прерывать спящий вызов.

Если вы ищете чистый 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), где тревога не срабатывает.прервать сон.

0 голосов
/ 28 июля 2011

попробуй

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

Это будет спать в течение 1 секунды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...