AnyEvent でイベント駆動プログラミング

昨年の YAPC で非同期系のセッションが人気があったので、ちょっと遅いけど、手を出してみることにする。

AnyEvent を小2時間ほどいじってみたので、自分なりの解釈で記事を書くことにする。

イベント駆動プログラミングって何?

プログラムが上から下に実行される、いわゆる手続き型プログラミングとは違い、イベントハンドラにコールバック関数を登録しておき、イベントループを回します。イベント発生をトリガとして、イベントハンドラに登録されているコールバック関数を実行するプログラミング手法。

ちなみにイベントとは以下のようなことを指します。

  • キーボード押された
  • マウスがクリックされた


Windows プログラミングの経験者であれば、すんなり入っていくことができると思います。GetMessage のイベントループとか WindowProc とか例のやつです。僕も、Windows プログラミングの経験者だったので、すんなり入ることができました。

初めての AnyEvent

1秒毎に現在時刻を表示し、標準入力(STDIN)から「quit」が入力されるまで、監視をするプログラムのサンプルです。

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw/say/;
use AnyEvent;

# 1秒ごとに現在時刻を表示するイベント
my $cvt = AnyEvent->condvar;
my $t;
   $t = AnyEvent->timer(
    after    => 0,
    interval => 1,
    cb => sub {
        say AnyEvent->time;
        $cvt->send;
    },
);
$cvt->recv;

local $| = 1;

# 標準入力から入力イベント
# quit が入力されるとイベントループを抜ける
IO:
for (;;) {
    my $cv = AnyEvent->condvar;
    my $wait_for_input;
       $wait_for_input = AnyEvent->io(
        fh   => \*STDIN,
        poll => 'rl',
        cb   => sub {
            chomp(my $input = <STDIN>);
            undef $wait_for_input;
            $cv->send($input);
        }
    );
    my $input = $cv->recv;
    say "[$input]";
    last IO if defined $input && $input eq "quit";
}

undef $t;
say "done.";

ソースコードの前半では、タイマーのイベントを作成しています。interval で指定した秒数毎に登録してあるコールバック関数が呼び出される仕組みになっています。

ソースコードの後半では、標準入力を監視するイベントを作成しています。標準入力から入力があったときに、登録してあるコールバック関数が呼ばれる仕組みになっています。

ソースコード内に度々でてくる、AnyEvent->condvar は、状態変数と呼ばれるもので、メインループに対してイベントの状態を通知するための変数です。

$cv->send で状態変数を変更し、$cv->recv は状態変数が変更されるまで、つまり、$cv->send が呼び出されるまでコードをブロックしています。明快な説明じゃないですね。。ちゃんと説明できるようになったら修正しよう。

ちなみに実行してみるとこんな感じになります。

% perl ae3.pl
1269878022.06494
1269878023.06862
1269878024.0677
amar1269878025.06774
i3
[amari3]
1269878026.06878
1269878027.06878
quit
[quit]
done.

勉強を始めたばっかりで、説明に誤りがあるかもしれないけど、おもしろいので引き続き勉強していきます。