perlメモ:alarmを使ったタイムアウト処理

perlでalarmを使ってタイムアウト処理を行う方法。割り込みとか、アラームとか、タイマーとかタイムアウト処理とか言ったりします。
perlでWebをGETするプログラムを書いたりすると、相手のサーバーが死んでいると、応答が無くなってプログラムが止まったままにになったりします。WebのGETにかかわらず、通信関係ではよくあることですが、例えば複数のURLを定期的にチェックするプログラムをperlで書いたときにチェックしてるURLのうちの一つでもサーバーが落ちたりしてると応答が帰ってこなくて他のURLまでチェックできなくなってしまったりします。これでは困るので、タイムアウト処理が必要になるのですがその場合に重宝するのがalarmです。


perl.comのサンプルソースをチョットいじったサンプルです。
下記のサンプルだと実行後while(1)の所で無限ループに陥ってしまいますが、その前で
alarm 10でalarmの設定を行っているので10秒後アラームシグナルによる割り込みが発生し
evalで囲まれた範囲の処理を抜けます。
$SIG{ALRM}の行は、割り込みでevalを抜けた際に$@に格納される文字を指定しています。
実際eval{};を抜けたあと$@の内容を表示するとしたのサンプルでは「alarm」が格納されているのが確認できます。
eval{};を抜ける直前のalarm 0;はalarmの解除です。問題なくeval{};で囲まれた範囲を終わるので最後にアラームによる割り込みを解除しています。
追加でeval{};を抜けた直後にもalarm 0;を書いている場合があるソースを見かけますが、alarm以外の原因で抜けた場合などの為に念の為に書いているのだと思います。
eval{};を抜けたあとは、 $@の中身があるかチェックして、正常にeval{};を抜けたのかチェックします。$@の中身があれば、何らかの割り込みでeval{};を抜けているので$@の中身をチェックしてeval{};を抜けてきた原因をチェックします。下記の例では$@の中身が「alarm」かどうかチェックしてalarm以外で抜けてきている場合は、dieでプログラムを終了しています。

eval {
local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
alarm 10;
my $cnt=0;
while(1){
print "テストループ:",++$cnt,"\n";
sleep(1);
}
alarm 0;
};
if ($@) {
die unless $@ eq "alarm\n";   # propagate unexpected errors
print "time out [$@]\n";
# timed out
}else{
print "didn't\n";
# didn't
}

通信処理などに上記を応用する場合は、送受信処理の部分をeval{};でくくって、eval{};のはじめの方でタイムアウト秒数を指定、eval{};を抜ける直前でalarm 0;によりタイマーをクリアするという流れでいいかと思います。
実際perl.comのサンプルもソケットをsysreadしているサンプルですので詳しくはそっちを見てください。
▼perl.com alarm
http://www.perl.com/doc/manual/html/pod/perlfunc/alarm.html

(Visited 517 times, 1 visits today)

タグ :