またまた非同期ブームに乗ってお勉強継続中です。
AnyEventとCoro::AnyEventの違い
AnyEventとCoro::AnyEventでは、メインループを回すスレッドが違います。
use strict; use warnings; use AnyEvent; use Coro; # use Coro::AnyEvent; my $done = AE::cv; my $t = AE::timer 0, 0, sub { print $Coro::current->{desc}, "\n"; $done->send; }; $done->recv;
このまま動かすと出力は「[main::]」で、Coro::AnyEventを有効にすると「[AnyEvent idle process]」となります。
裏でメインループが回る
Coro::AnyEventを有効にすると idle 状態でループが回るようになるので、 recv しなくてもループを回すことができます。具体的には、readyなスレッドがないときにscheduleすればOKです。 Coro::* シリーズが明示的にAnyEventのメインループを回さなくてもうまく動いているのは、この原理を利用してCoroのidleプロセスが裏でメインループを回しているからです。
以下の例では、condvarの代わりにメインスレッドのCoroを終了のフラグに使っています*1。condvarを使ったときとほぼ似たようなコードですね。
【9/24追記】readyは別スレッドから勝手に呼べるので、$done などのフラグを用意して schdule while !$done; とかの方がよりよいと思います。
use strict; use warnings; use AnyEvent; use Coro; use Coro::AnyEvent; my $main = $Coro::current; my $t = AE::timer 0, 0, sub { print $Coro::current->{desc}, "\n"; $main->ready; }; schedule;
メインループをブロックしない
昨日のエントリでrouse_waitの使い方を書きましたが、メインループを回しているスレッド([AnyEvent idle process])からrouse_waitを呼ぶと不幸なことになります*2。rouse_waitでメインループが進むことを期待しているのに、メインループをブロックしているのは自分だからです。
schedule に関しても同様です。メインループが $Coro::idle ですので、readyなスレッドがないと自己循環な呼び出しになってしまいます*3。
メインループとメインスレッドは違う
ちょっとややこしいのは、Coro::AnyEventの環境だと「メインループを回しているスレッドとメインスレッドが違う」ということです。言葉の使い方を気をつけないと、情報交換に支障が出たり変なとこでハマったりしそうです。