Perl AnyEvent回调延迟子,如何运行它异步



我开始学习AnyEvent,并且遇到了一些问题。我完全误解了怎么可能获得异步利润,fe:

#!/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秒。所以,根本没有利润。

为什么是这样,我如何(可能吗?)创建"正确的"回调?

除非在等待事件完成时阻塞顶层程序,否则不要使用condvar。使用condvar使得重用代码变得非常困难;任何内部有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上的begin和end,但永远不会阻塞程序。所以重用使用它的代码很容易。此外,事件是命名的,因此您可以将结果收集为散列,而不是在调用其他回调函数时仅调用回调函数。

$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 $namen"; $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;

最新更新