モダンPerlの世界へようこそ

第13回 AnyEvent:イベント駆動モジュールの方言を吸収する

この記事を読むのに必要な時間:およそ 4.5 分

イベントループを持つモジュールが抱える問題点

イベントループを持つモジュールの一例として,前回POEを取り上げましたが,もちろん同じようなループを持つモジュールはほかにもあります。

たとえば1995年に故ニック・イング・シモンズ(Nick Ing-Simmons)氏が始めたPerl/Tkや,POE誕生前夜の1997年から開発が行われているGtk(のちのGtk-Perl),その後継にあたるGtk2/GlibのPerlバインディング(2003年)など,GUIアプリケーション関連のツールがそうですし,IO::Poll(1997年)IO::Async(2007年)のようなモジュールにもイベントを監視するためのループが使われています。また,Event(1997年)やlibeventのラッパであるEvent::Lib(2004年),EV(2007年)のように,イベントループそのものを実装するためのモジュールを使えば,自分でイベントループを持つモジュール/アプリケーションをつくることもできます。

ただし,このようなモジュールがイベントを監視し続けるために使う「メイン」ループには,同時にひとつしか存在しえないという制約があります。そのため,複数のイベントモジュールを同時に扱うことは,できないか,できても非常に面倒なことになるのが常でした。

たとえば,Tkを使ったGUIアプリケーションの裏で時間のかかる処理を非同期に(GUI部分を操作不能にしないように)実行しようと思ったら,前回紹介したように,ふつうは処理ばらして,ときどきTkのイベントループに処理を戻すような形で書くことになりますが,そうすると,既存の(実績があってもTkのことを考慮していない)モジュールは使いづらくなってしまいますし,アプリケーションを別のイベントモジュール,たとえばGtk2/Glibを使うように移植するときにはその非同期処理も同様にTkベースからGtk2/Glibベースに移植しなおさなければならない,といった問題が起きてしまいます。

このような無駄な手間をなくすために生まれたのが,2004年にマーク・レーマン(Marc Lehmann)氏が開発を始めたAnyEventです。

GUIアプリのための?AnyEvent

連載第4回で取り上げたAny::Moose同様,AnyEventにも「どんなイベントモジュールにも対応できる」という含みがあるのですが,ほかのAny系のモジュールと違って,AnyEventは,本質的には自前のメインループを持つイベントモジュールではありません。ループそのものは別の(既存の)イベントモジュールにまかせたうえで,そのループ上で実行する各種のイベントを,イベントモジュールに依存しない形で書けるようにする,というのが本来の用途です。

もっと具体的にいうと,TkやGtk2を使ったGUIアプリケーションの裏で,Coroという,レーマン氏が2001年に(当時ActiveState社とMicrosoft社がおもにWindows環境でforkをエミュレートするために開発していたithreadに対抗するかのように)書いたコルーチン(軽量スレッド)モジュールを使って非同期処理を簡単に行うためのインタフェースといってもよいでしょう。

2005年の初公開当時,AnyEventはEvent,Coro,Tk,Glibにしか対応していませんでした。Pure Perlで書かれた自前のメインループを持つようになったのは2006年秋のバージョン2.0から。2007年秋にC/XSでEVというイベントループを自分で実装したあと(バージョン2.55),ユーザから問い合わせのあったWxをサポートするための手段としてか,あるいは別の思惑があったのか,すでにWxに対応済みだったPOEへの対応を本格化させたのは2008年4月リリースの3.3以降のことです。

※1

ちなみに,レーマン氏は2003年に(2001年以降放置されていたGtk2移行前の)旧Gtk向けPerlバインディングの最後のリリースを行ったあと,Gtk2/Glibの最初期のバインディングのリリースも担当しています(~2003年11月)。旧Gtkへの対応が書かれていないのはこのような移行期の事情を承知していたためと考えておけばよいでしょう。

知られざる存在からの脱却

このように,当初はむしろGUIアプリケーション向けの特殊なモジュールとみなされていたためか,これまでAnyEventには独自のコミュニティといえるようなものは存在していませんでした。メーリングリストに投稿はなく(以前はほかのイベントモジュールともどもperl-loopというメーリングリストを共用していたようですが,ここもほとんど無名の存在だったようです),レーマン氏のサイトからアクセスできる公式IRCチャンネルも,常駐している人は10名いるかいないかという程度。先日日本の有志がirc.perl.org上にAnyEvent用のチャンネルを開設しましたが,本稿執筆時点で同氏の参加はなく,不満や要望がある場合は直接本人宛にメールを送るか(RT経由でチケットを登録すると怒られます),同氏が参加しているIRCチャンネルを探して話しかけるくらいしかないのが現状です。

その意味で,AnyEventはまだ「コミュニティに認められてこその『モダン』」という条件を満たしているとは言いがたいのですが,2008年春のPOE対応以降,AnyEventは各種イベントモジュールのアダプタとしてというより,POEよりもシンプルで高速なイベントモジュールとしてその存在感を高めてきているようです。

2008年6月にポルトガルで開催されたワークショップではAnyEventのベンチマーク結果を紹介する発表があったほか,最近では2009年2月にフランクフルトで開催されたワークショップや,2009年5月にモスクワで開催されたYAPC::Russia2009年6月にピッツバーグで開催されたYAPC 10,そして2009年9月に開催されるYAPC::AsiaでもAnyEvent関連のトークが予定されています。

そこで,ここではひとまずGUIアプリケーションのことは忘れて,AnyEventを(付属のPure Perlのメインループとともに)独立したイベントモジュールとして使う例と,メインループにEV,追加の非同期処理にCoro,そして両者をつなぐ架け橋としてAnyEventを使う例をそれぞれ簡単に紹介してみます※2)。

※2

なお,現状ではコミュニティが小さくフィードバックも乏しいため,AnyEventやCoroは環境によってうまくインストールできなかったり,一部の機能が利用できないことがあります。特にWindows環境の場合(古めのPerlを使っている場合は特に)うまく動作しない可能性が高いので,必要に応じて仮想マシンを用意するなりPerlのバージョンをあげるなりしてください。

AnyEvent単体でのイベントプログラミング

AnyEventのもっとも単純な使い方として,まずは標準入力を監視するだけのスクリプトを書いてみます。Windows環境でも動かしやすいよう,ここではあえてPure Perlのアダプタを使うように明示的に宣言しています。

#!perl
use strict;
use warnings;
use AnyEvent::Impl::Perl;
use AnyEvent;

my $cv = AnyEvent->condvar;
my $io; $io = AnyEvent->io(
    fh   => \*STDIN,
    poll => 'r',
    cb   => sub {
        chomp(my $input = <STDIN>);
        undef $io;
        $cv->send($input);
    },
);

if (defined(my $input = $cv->recv)) {
    print "got: [$input]\n";
}

いささか見慣れない表記もありますが,まず,$cvで受けているのは,ほかのライブラリではstateなどとも呼ばれる状態変数(オブジェクト)です。AnyEventではイベントを監視するWatcherでコールバック関数が実行された場合,この$cv(condvar)を通じてメインループにその旨を伝えることになっています。

次の$io = AnyEvent->io(...) の部分がWatcherを用意している部分。my $io = AnyEventy->io(...) と書かず,あえて my $io; $io = AnyEvent->io(...) とふたつに分けて書いているのは,コールバックのなかでWatcherをundefしたいから。詳しくはperldoc perlsubでmy () によるプライベート変数の話題を扱っている箇所をご覧ください。

ここではファイルハンドル(STDIN)の読み込みを監視して,値が入ってきたらcbで指定されているコールバック関数を呼び出し,そのなかでSTDINからデータを受け取ったあと,使い終わったWatcherを無効にして(これ以上イベントが呼び出されないようにして),状態変数を通じて受け取った値を外部に返す,という形になっています。

著者プロフィール

石垣憲一(いしがきけんいち)

あるときは翻訳家。あるときはPerlプログラマ。先日『カクテルホントのうんちく話』(柴田書店)を上梓。最新刊は『ガリア戦記』(平凡社ライブラリー)。

URLhttp://d.hatena.ne.jp/charsbar/

コメント

コメントの記入