2011-07-26 9 views
3

私はAnyEventを学び始め、いくつかのトローブを持っています。 I完全に誤解どのようにその可能性を得るために、非同期利益、FE:出力でPerl AnyEventコールバックの待ち時間サブで、どのように非同期を実行しますか?

#!/usr/bin/env perl 

package LatencySub; 

use strict; 
use warnings; 
use AnyEvent; 

# sub for emulate latency - is it right way? 
sub do_delay{ 
    my ($name, $delay) = (@_); 
    my $cv = AE::cv; 
    my $timer = AE::timer $delay, 0, sub { $cv->send() }; 
    $cv->recv; 
    return $name.' proceed, delay is '.$delay; 
}; 


package main; 

use 5.12.0; 
use warnings; 

use Smart::Comments; 

use AnyEvent; 

my @list = (
    { name => 'first', delay => 1 }, 
    { name => 'second', delay => 1 }, 
    { name => 'third', delay => 2 } 
); 

sub process_cb { 
    my ($name, $delay, $cb) = @_; 
    my $result = LatencySub::do_delay($name, $delay); 
    $cb->($result); 
} 

my %result; 

my $cv = AE::cv; 
# outer loop 
$cv->begin (sub { shift->send (\%result) }); 

my $before_time = AE::time; 
### foreach start... 
foreach my $entity (@list) { 
      $cv->begin; 
      process_cb ( 
           $entity->{'name'}, 
           $entity->{'delay'}, 
           sub { 
            $result{$entity->{'name'}} = shift; 
            $cv->end; 
           } 
      ); 
    } 
### foreach end... 

$cv->end; 
my $time_all = AE::time - $before_time; 

### $time_all 
### %result 

は、私が得た:

### foreach start... 

### foreach end... 

### $time_all: '4.02105116844177' 
### %result: { 
###   first => 'first proceed, delay is 1', 
###   second => 'second proceed, delay is 1', 
###   third => 'third proceed, delay is 2' 
###   } 

すべての遅延和(1 + 1 + 2)EQ $ time_all - 4秒。 それで、全く利益はありません。

「正しい」コールバックを作成するにはどうすればいいですか(それは可能でしょうか)。

答えて

4

イベントが完了するのを待っている間、あなたのトップレベルのプログラムをブロックすること以外condvarsを使用しないでください。 condvarsを使用すると、コードを再利用することが非常に困難になります。内部的にcondvarを持つ関数は、それにcondvarを持つ別の関数を持つプログラムで決して安全に使用することはできません。 (あなたがrecvを呼び出していないだけcbを使用しない場合、これは真実ではありません。しかし、まだ...それは、彼らがやっているかわからないもののために危険だとはない。。)

マイルール:ファイル名が.pmを終了した場合、条件なし!あなたは、並列に複数のものを実行して、一度、すべての結果が利用可能ないくつかのより多くのコードを実行したい場合は

Event::Joinしてみてください:

sub delay($$) { 
    AnyEvent->timer(after => $_[0], cb => $_[1]); 
} 

my $join = Event::Join->new(
    on_completion => sub { say "Everything is done" } 
    events  => [qw/t1 t2 t3/], 
); 

delay 1, $join->event_sender_for('t1'); 
delay 2, $join->event_sender_for('t2'); 
delay 3, $join->event_sender_for('t3'); 

その後、3秒後に、あなたは「すべてが行われ、」表示されます。イベント:: Joinは、condvarsのbeginとendのようなものですが、決してプログラムをブロックすることはできません。したがって、それを使用するコードを再利用するのは簡単です。また、イベントには名前が付けられているので、他のコールバックが呼び出されたときにコールバックを呼び出すのではなく、結果をハッシュとして収集することができます。

5

と呼ばれる$cv->recv->sendまでブロックしますコールは、そうdo_delay()は返すように$delay秒かかります。ここで

は、3つのスレッドを産卵し、完了するために、それらのすべてを待っているの例です。

use strict; 
use warnings; 
use AnyEvent; 

sub make_delay { 
    my ($name, $delay, $cv) = (@_); 
    $cv->begin; 
    return AE::timer $delay, 0, sub { warn "done with $name\n"; $cv->end }; 
} 

my $cv = AE::cv; 

my @timers = (make_delay("t1", 3, $cv), 
       make_delay("t2", 5, $cv), 
       make_delay("t3", 4, $cv) 
); 

$cv->recv; 
+0

Aha!それは私が望むものにもっと似ています。なぜ私がそこに必要なのかを理解することは非常に重要だと思います。$ cv-> begin; my $ timer = AE :: timer $ delay、0、sub {warn "$ name \ n"; $ cv-> end}; return $ timer; " (単にシンプルなビューのために)返すAE :: timerオブジェクト? – Meettya

+1

perldoc AnyEventで$ cv-> beginのドキュメントを参照してください。 $ cv-> beginはカウンタをインクリメントし、$ cv-> endはカウンタをデクリメントし、カウンタが0になると条件変数を起動します。 また、AE :: timerオブジェクトを保存して、範囲。 – perlman

関連する問題