Fork::setTimeout

[g:subtech:id:cho45:detail](http://subtech.g.hatena.ne.jp/cho45//detail)さんが、AnyEventでJavaScriptのsetTimeout()的なものを書いていたので([AnyEvent::setTimeout - 冬通りに消え行く制服ガールは✖

g:subtech:id:cho45:detailさんが、AnyEventでJavaScriptのsetTimeout()的なものを書いていたので(AnyEvent::setTimeout - 冬通りに消え行く制服ガールは✖夢物語にリアルを求めない。 - subtech)、forkで同じようなことをしてみました。

use Fork::setTimeout;
my $timer = setTimeout(sub {
warn "1sec!";
}, 1000);

とか書けます。setTimeout()にわたしたcoderefをforkした子プロセスで実行しつつタイマーオブジェクトを返して、そいつがスコープを抜けるタイミングで適当にwaitpidしたりします。

が、以下のようなコードがうまくいかない。どうしたらいいんだろう。

my $i = 0;
setTimeout(sub { $i++ }, 1000);
sleep 3;
warn $i; #=> 0

$iは1になってほしい。

追記

.@antipop それ Test::SharedFork のアレでできるよ

2011-07-26 09:12:17 via Echofon

forkなんだから共有しなくていいんじゃないの。したいときだけtieとかIPC::*なんちゃらとか。

2011-07-26 10:39:59 via TweetIrcGateway

諸氏に教えをいただいた。

上記の$iは、子プロセスにコピーされるので親プロセスから見ると0になっちゃうのは当然。プロセス間でデータを共有したい場合は、たとえばIPC::Shareableを使ってshared memoryなりなんなりを通して共有するとよいようだ。というわけで、以下のように書くと期待通りにはなりました。

use IPC::Shareable;
my $handle = tie my $i, 'IPC::Shareable', undef, { destroy => 1 };
$i = 0;
my $timer5 = setTimeout(
    sub {
$handle->shlock;
$i++;
$handle->shunlock;
}, 1000
);
sleep 3;
warn $i; #=> 1

参考: Parallel::ForkManager + IPC::Shareable で複数のプロセスで変数を共有する

追々記

!id:nihenさんが共有データのIPCをすることなく、いい感じにcallbackを実行するやりかたを書いてくださいました。

SIGCHLDで実行するのですねー。なるほどなるほどー。勉強になります。