ブロッキングする処理を外部プロセスに任せる - AnyEvent::Workerを使ってみた
AnyEventを使う場合に、どうしてもブロッキングしてしまうような処理があるとして、それを外部プロセスとして切り出しつつ、メインのイベントループの中に取り込みたいんだよな、と。
そんな時はAnyEvent::Workerがよさそうです。
AnyEvent::Worker - Manage blocking task in external process
http://search.cpan.org/~mons/AnyEvent-Worker/
POEで言うところのPOE::Component::Genericのようなものらしいです。使いこなせるようになるといろいろと便利!
use strict; use warnings; use AnyEvent; use AnyEvent::Worker; $|++; print _timestamp(), "開始しまーす\n"; my $cv = AnyEvent->condvar; print _timestamp(), "AnyEvent::Workerで外部プロセス化", "\n"; my $worker = AnyEvent::Worker->new( ['My::App'] ); print _timestamp(), "ブロッキングする処理を投げる", "\n"; $worker->do( blocking_task => 'a happy new year!', sub { my ( $worker, $return_message ) = @_; print _timestamp(), "外部プロセスからかえってきたよ", "\n"; $cv->send($return_message); } ); print _timestamp(), "その間にメインのイベントループでは他の処理ができるんだぜ", "\n"; sleep 2; print _timestamp(), "メインループでもわざと2秒眠ってみたフリ", "\n"; my $rec = $cv->recv; print _timestamp(), $rec, "\n"; sub _timestamp { my ( $sec, $min, $hour, $day, $month, $year ) = localtime(time); sprintf("%02d時%02d分%02d秒\t", $hour, $min, $sec); } #---------- package My::App; sub new { my $class = shift; my $self = bless {@_}, $class; return $self; } sub blocking_task { my $self = shift; my $message = shift; for ( 1 .. 5 ) { print "\t", _timestamp(), "外部プロセスでブロッキングしてしまう処理", "\n"; sleep 1; } return $message; } sub _timestamp { my ( $sec, $min, $hour, $day, $month, $year ) = localtime(time); sprintf("%02d時%02d分%02d秒\t", $hour, $min, $sec); }
これを実行するとこうなります。
インデントして書いてある行は外部プロセスで実行した処理です。
imac:Hacks miki$ perl any_event_worker.pl 07時55分09秒 開始しまーす 07時55分09秒 AnyEvent::Workerで外部プロセス化 07時55分09秒 ブロッキングする処理を投げる 07時55分09秒 その間にメインのイベントループでは他の処理ができるんだぜ 07時55分09秒 外部プロセスでブロッキングしてしまう処理 07時55分10秒 外部プロセスでブロッキングしてしまう処理 07時55分11秒 メインループでもわざと2秒眠ってみたフリ 07時55分11秒 外部プロセスでブロッキングしてしまう処理 07時55分12秒 外部プロセスでブロッキングしてしまう処理 07時55分13秒 外部プロセスでブロッキングしてしまう処理 07時55分14秒 外部プロセスからかえってきたよ 07時55分14秒 a happy new year!
外部プロセスでは1秒スリープを5回まわしてます。
なので「ブロッキングする処理を投げる」から5秒経って「外部プロセスからかえってきたよ」が表示されてますね。
注目すべきはその5秒の間にメインのイベントループの中でも他の処理ができているという部分です。
これがAnyEvent::Workerの良いところですね。ふんづまる処理は外部プロセスに押し出してしまおう、という発想です。
あ、ちなみにメインのイベントループで2秒スリープしているのは演出のため、わざとです。
実際にはブロッキングするような処理をAnyEventの中で書いては意味ありませんので、そこんところヨロシク。
そして最後に「外部プロセスからかえってきたよ」「a happy new year!」ということで、
新年あけましておめでとうございます。今年もよろしくござ候ー。