2011-07-26 59 views
3

我開始學習AnyEvent,並有一些與它的事情。 我完全誤解了怎麼它可能獲得異步利潤,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使得重用代碼非常困難;任何內部具有condvar的函數都不能安全地用於另一個函數具有condvar的程序中。 (如果你從來沒有打電話recv,並且只使用cb這是不正確的,但仍然...這是危險的,而不是爲那些不知道自己在做什麼。)

我的規則:如果文件名結束.pm ,沒有condvars!

如果你想在並行運行多個事物和運行更多的代碼,一旦所有的結果都可以,嘗試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秒鐘後,你會看到「一切都做」 。 Event :: Join就像condvars的開始和結束一樣,但永遠不能阻止你的程序。所以很容易重用使用它的代碼。此外,事件被命名,因此您可以收集結果作爲散列,而不是在調用其他回調時調用回調。

5

電話$cv->recv將被阻止,直到調用->send,所以do_delay()需要$delay秒返回。

這裏產卵三個線程,等待他們全部完成的示例:

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

啊哈!這更像是我想要的。 「$ cv-> begin; my $ timer = AE :: timer $ delay,0,sub {warn」done with $ name \ n「; $ cv-> end};我認爲它非常重要。返回$ timer;「 (只是爲了簡單查看)返回AE :: timer對象? – Meettya

+1

請參閱perldoc AnyEvent中$ cv-> begin的文檔。 $ cv-> begin遞增計數器,$ cv-> end遞減計數器並在計數器達到0時觸發條件變量。 此外,您還需要保存AE :: timer對象,以便它們不會超出範圍。 – perlman