2011-07-26 32 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