web-dev-qa-db-ja.com

Perlでタイムアウトを行う方法は?

私は頻繁に次のパターンを使用して、Perlで特定のコードフラグメントの実行時間の上限を設定します。

my $TIMEOUT_IN_SECONDS = 5;
eval {
    local $SIG{ALRM} = sub { die "alarm\n" };
    alarm($TIMEOUT_IN_SECONDS);
    # do stuff that might timeout.
    alarm(0);
};
if ($@) {
    # handle timeout condition.
}

私の質問:

  • これは正しい方法ですか?
  • 実行時間が$ TIMEOUT_IN_SECONDSを超える可能性がある状況はありますか、または上記のメソッドは防弾ですか?
20
knorv

おそらく Sys :: SigAction を見たいと思うでしょう。私自身は使ったことがありませんが、 いくつかの熱烈なレビュー があります。

注意すべき点の1つは、「タイムアウトする可能性のあるもの」がsleepまたはalarm自体を使用するかどうかです。また、エラー処理コードでは、タイムアウト以外のエラーに備えていると思います。

10
cjm

Time :: Out を試すこともできます。私は構文が好きで、ネストされたタイムアウトがサポートされています。

4
Peter V. Mørch

シグナルの取り扱いには注意してください。 Perlはシグナルを非同期で受信し、別のシグナルがコールバックによって処理されている間にシグナルを受信すると、シグナルが失われたり、互いに干渉したりする可能性があります。

イベント処理ライブラリのWin32サポートはPerlではかなりまあまあです(私は非cygwin Win32をサポートする必要があります)ので、私は通常、タイムアウトに単純なポーリングループを使用します。

use Time::HiRes qw(sleep);

sub timeout {
  my $timeout = shift;
  my $poll_interval = shift;
  my $test_condition = shift;
  until ($test_condition->() || $timeout <= 0) {
    $timeout -= $poll_interval;
    sleep $poll_interval;
  }
  return $timeout > 0; # condition was met before timeout
}

my $success = timeout(30, 0.1, \&some_condition_is_met);

スリープタイマーは、ユーザーまたは呼び出し元で簡単に構成できます。非常にタイトなループを実行している場合や、ループで複数の呼び出し元が待機している場合(レースやデッドロックが発生する可能性がある場合)を除いて、シンプルで信頼性があります。 、およびタイムアウトを実装するためのクロスプラットフォームの方法。

また、ループオーバーヘッドは、タイムアウトが完全に守られることを保証できないことを意味することに注意してください。 $ test_condition、デクリメント、ガベージコレクションなどが干渉する可能性があります。

2
Jeff Ober