AnyEventはじめました

何故かPOEをリファクタリングする指令を受けたのでなんとなくAnyEventを練習しています。
というわけで復習を兼ねての記事です。

サンプルコード

package Model::Person;
use strict;
use warnings;
use Carp ();
use Class::Accessor::Lite;
Class::Accessor::Lite->mk_accessors(qw/name hp/);

sub new {
    my $class = shift;
    my %args = @_ == 1 ? %{$_[0]} : @_;
    bless {%args}, $class;
}

sub beat {
    my ($self, $person) = @_;
    Carp::croak("This is NOT a Model::Person...")
      unless $person->isa('Model::Person');

    $person->lose_hp(1);
}

sub lose_hp {
    my ($self, $damage) = @_;
    my $rest_hp = $self->hp - $damage > 0 ? $self->hp - $damage : 0;
    $self->hp($rest_hp);
}

sub is_crying { return $_[0]->hp == 0 ? 1 : 0 }

1;

package main;
use strict;
use warnings;
use AnyEvent::Impl::Perl;
use AnyEvent;

my $jojo = Model::Person->new( name => 'jojo', hp => 10_000 );
my $dio  = Model::Person->new( name => 'dio', hp => 10_000 );

### create condition variable
my $cv = AnyEvent->condvar;

### create watcher
my $w1;
$w1 = AnyEvent->idle(
    cb => sub {
        print "オラ";
        $jojo->beat($dio);
    }
);

my $w2;
$w2 = AnyEvent->timer(
    after => 0,
    interval => 1,
    cb   => sub {
        if ( $dio->is_crying ) {
            undef $w1;
            undef $w2;
            $cv->send();
        }
        else {
            print "\n\n(泣くまで止めない!!)\n\n";
        }
    },
);

print "君がッ\n";
print "泣くまで\n";
print "殴るのをやめないッ\n";

sleep(1);

$cv->recv;

状態変数がッ!!真になるまで!!

AnyEventをuseしただけではイベントループが始まるわけではありません。
イベントループが開始されるタイミングは次の箇所です。

$cv->recv;

この時点でスクリプトの処理がブロックされて、代わりにイベントループが開始されます。

状態変数である$cvが真になるまでこのイベントループは続きます。状態変数が真になるのは次の処理です。

$cv->send();


ちなみに今回このイベントループの中には二つのイベントを用意しています。

  • Timer
  • Idle

Timerは1秒起きに実行されます。Idleは他にやることがない時に行う処理です。
watcherと呼ばれる$w1,$w2でイベントを登録しています。
このイベントで何が起きるかを無名関数を使って用意してあります。

この中でイベントを抜けるタイミングを調整します。イベントを抜ける時にwatcherが不要になるのでundefで明示的にメモリ開放しています。

というわけで

今回は状態変数が1つだけなので割と簡単にAnyEventを書けました。

↓2010-11-15 14:10修正

次回は状態変数が複数ある場合イベントループを抜ける条件が複数ある場合、を考えたいと思います。

To be continue...