10

假设我在一个应用程序中有许多任务可以按任何顺序完成。当所有任务完成后,我需要运行一些代码。如果这很重要,应用程序在 AnyEvent 下运行,但没有 Coro。

在某种程度上,AnyEvent$cv->begin/$cv->end允许我想要的。但是,我希望有更细粒度的控制。例如,我希望不能两次“完成”一项任务。从所有任务中收集数据的能力也很好。

当然,这是可以做到的。设置许多共享哈希的回调;每当任务完成时从该哈希中删除键;当 hash 为空时调用 megacallback。我想知道是否有更文明的方法,也许是一些 CPAN 模块?

例如,这是一个可以满足我需要的虚构 API。

#!/usr/bin/perl -w 
use strict;

use Some::Module;

# Set goals
my $cb = Some::Module->new( sub { say 'BOOM!' } );
$cb->begin( qw(foo bar) );

# Much later, as tasks start getting done
$cb->end( foo => 42 );       # "return" value from task 'foo'
$cb->begin( 'baz' );         # can add more tasks, why not
$cb->end( 'bar' );           # just finish task 'bar'
# still waiting for 'baz' to finish at this point

# Finally, last hanging task is done
$cb->end( baz => 137 );      # BOOM!
# at this point, sub {}->( { foo=>42, bar=>undef, baz=>137 } ) 
#     has been called

另请参阅我的perlmonks 问题

它有这样的东西吗?

4

2 回答 2

3

您可能需要考虑Future

具体来说,为了等待许多事情完成,您可以使用Future->needs_all或类似:

my @things = ... # generate Futures to represent each thing

Future->needs_all( @things )
  ->on_done( sub {
     # some code here when all the things are done 
  });

或者,您也可以尝试Async::MergePoint,它使 API 更接近您的想法:

my $mp = Async::MergePoint->new( needs => [qw( foo bar )] );
$mp->close( on_done => sub {
   # some code here when all the things are done
});

$mp->done( foo => $value );
$mp->done( bar => );
于 2013-04-05T22:55:59.180 回答
2

我当然不是异步方面的专家,但我认为Mojo::IOLoop::DelayMojolicious套件的一部分)具有类似的界面。请注意,Mojo::IOLoop 可以与EV 一起使用,因此可以与 AnyEvent 一起使用。

这是食谱中的一个示例:

use Mojo::UserAgent;
use Mojo::IOLoop;

# Synchronize non-blocking requests portably
my $ua    = Mojo::UserAgent->new;
my $delay = Mojo::IOLoop->delay(sub {
  my ($delay, $tx, $tx2) = @_;
  ...
});
$ua->get('http://mojolicio.us'         => $delay->begin);
$ua->get('http://mojolicio.us/perldoc' => $delay->begin);
$delay->wait unless Mojo::IOLoop->is_running;

另外,请注意,$delay->begin返回本质上是end方法的回调。

::Delay 文档中显示了其他示例,例如很酷的“步骤”概念。

编辑

这是一个简单的例子。请注意,延迟类中刚刚发生了一个小的语法更改,因此这只适用于 Mojolicious 3.93+,并不是说这在以前是不可能的,但语法略有不同。

#!/usr/bin/env perl

use strict;
use warnings;
use v5.10;

use Mojo::IOLoop;

my $delay = Mojo::IOLoop->delay(sub{shift; say for @_});

my $end_foo = $delay->begin(0);
Mojo::IOLoop->timer( 0 => sub { $end_foo->('hi') } );

my $end_bar = $delay->begin(0);
Mojo::IOLoop->timer( 0 => sub { $end_bar->('bye') } );

$delay->wait unless $delay->ioloop->is_running; #start loop if necessary

这里我们创建延迟对象,参数是一个finish事件回调。对于我调用的每个异步操作begin,它都会返回一个end回调。默认情况下,这些回调会删除它们的第一个参数以删除多余的调用者(参见上面的示例),但我们没有这些,所以我们通过0表示不这样做。对于每个动作,我只是用一个零等待计时器来推迟。然后,结束回调的参数按顺序排队等待结束事件。多田!

于 2013-04-05T23:06:26.787 回答