2

他のいくつかの関数を呼び出す関数があります。

sub do_work {
    send_mail();
    send_soap_envelope();
    send_rpc();
}

呼び出された関数がハングする可能性があるため、タイムアウト後に停止したいと考えています。私のコンテキストではコストがかかるため、フォークは避けたいです (たとえば、フォークのたびにデータベースハンドルを再作成する必要があります)。私は次のスキームを思いつきました:

sub timeout {
    my ($code) = @_;
    eval {
        alarm 2;
        local $SIG{ALRM} = sub { die 'timeout' };
        &$code;
        alarm 0;
    };
    # handling of $@ eq 'timeout' removed for brevity
}

sub do_work {
    timeout \&send_mail;
    timeout \&send_soap_envelope;
    timeout \&send_rpc;
};

timeout()関数 (この例では 2 秒のタイムアウトにハードコードされています) は、をeval使用してペイロード関数の実行を中止する手段としてブロックを使用しdieます。

dieこれは私のテスト シナリオでは問題なく動作しますが、Perl インタプリタが「安全な状態」にないとき、たとえば XS サブルーチンを処理しているときに がペイロード関数を中断するとどうなるか不安です。私の腸は正しいですか?

4

3 に答える 3

5

5.8.1 以降、Perl は「安全なシグナル処理」を使用します。システムにシグナルハンドラーを提供するのではなく、代わりに安全なシグナルハンドラーを提供します。この安全なシグナル ハンドラーは、シグナルが受信されて返されたことを通知するだけです。Perl オペコードを実行する間に、インタープリターはシグナルが受信されたかどうかを確認し、シグナルが受信された場合はシグナル ハンドラーを呼び出します。

これは、長い XS 呼び出しや長い正規表現の一致など、長い操作の途中でシグナルが処理されないことを意味します。sleepシグナルはほとんどのシステム コールに割り込むため、ブロックしているシステム コール ( 、readなど)の最中であれば、シグナルが着信した直後にシグナル ハンドラが呼び出されます。

alarm(2);
my $s = time;
$SIG{ALRM} = sub {
   my $e = time;
   print $e-$s, "\n";  # 6, not 2.
};
('a' x 25) =~ /a*a*a*a*a*a*a*a*a*(?:b|c)/;

* — プログラムを高速化するために、チェックの頻度を少し減らしましたが、違いに気付かないはずです。

于 2012-06-13T05:59:58.277 に答える
1

ハンドラーをインストールする前に呼び出すため、安全ではありませんと行を入れ替えると、大幅に改善されるはずです。alarm()SIGALRMlocal $SIG{ALRM}alarm

于 2012-06-13T17:44:29.883 に答える
0

さて、今、それperldoc -f alarmが私の正確なユースケースに言及していることがわかります:

"alarm" を使用してシステム コールをタイムアウトさせたい場合は、"eval"/"die" のペアを使用する必要があります。

(そこにサンプルコードが続きます。)

于 2012-06-13T06:08:05.703 に答える