1

我有以下子例程,并且有 appx 20 个线程使用不同的 URL 调用它(这个子程序属于一个包,每个线程调用该包的不同实例):

sub get_urls {
    my ($self,$url,$depth) = @_;
    my $cv = AnyEvent->condvar;
    my @data;
    my %visited;
    my $hostname = URI->new($url)->host();
    my $tr_cb;
    my ($b,$e) = (0,0);

       return unless($depth);
       # This code-ref is recursive!
       $tr_cb = sub {
        my $sitem   = shift;
        my $depth   = shift;

        return if (0 == $depth--);
        foreach my $site (@$sitem) {
            if (exists($visited{$site})) {
               next;
            }
            $b++;
            $visited{$site} = 1;
            $cv->begin;
            AnyEvent::HTTP::http_get ($site, timeout => 1, sub {
               my ($body, $hdr) = @_;
               if ($hdr->{Status} =~ m/^2/) {
                  my $extor = HTML::SimpleLinkExtor->new();
                  my @links;
                  print "E = $e | B = $b\n";
                  #print "[REC_DEPTH:$depth]Working on $site\n";
                  $extor->parse($body);
                  @links = map { URI->new_abs($_,$site)->as_string }
                            grep { length > 2 } $extor->links();
                  push(@data,@links);
                  $tr_cb->([map { $_->[2] } 
                              grep { $_->[0] eq $_->[1] } 
                                map { [$hostname,URI->new($_)->host(),$_] } @links],$depth);
               }
                $e++;
                $cv->end;
            });
        }
        };
    $tr_cb->([$url],$depth);
    $cv->recv;
    print "Got total of " . @data . " links\n";
}

($b,$e)变量仅用于测试。问题是,一段时间后,“开始”的数量似乎与“结束”的数量不匹配,因此它永远不会通过$cv->recv......我对 AnyEvent 和事件编程一般来说有点新,似乎解决我的问题。

谢谢,

4

3 回答 3

3

整个递归匿名子程序似乎有点太聪明了。在您的类中创建一个函数,该函数从传入的 url(和深度)获取链接并将它们添加到对象的数组中。同时创建一个计时器(在 => 0 之后),它将元素从数组中移出,如果仍有元素则重新启动,否则将 end 发送到 condvar。如果需要,将数组替换为 Thread::Queue 对象。

您还应该只在应用程序代码中调用 ->recv,而不是在库中调用,或者在 condvar 上使用回调而不是调用 recv(这将使您能够使用多个 condvar 并在不依赖线程的情况下发送给它们)

于 2012-08-26T08:18:47.443 回答
1

这个实用函数的设计错误在于它阻塞了 condvar: $cv->recv。这会阻止该函数的用户在全局异步程序中使用它。

相反,get_urls应该返回 condvar ( $cv) 并让用户用它做一些有用的事情。如果用户想要阻止,他将能够做到。如果他不这样做,他将可以自由地与其他异步任务共享资源。

您的程序的另一个问题是,如果该函数设计正确,您可能不需要线程:如果该函数是程序的核心,那么它显然是网络绑定的,而不是 CPU 绑定的,因此您应该能够启动多个调用(固定)get_urls将在单个线程中并行执行。

于 2012-09-04T16:18:54.167 回答
-2

你有$cv->end;你的回调。对我来说似乎不对,应该是在AnyEvent::HTTP::http_get通话后,AFAIU。

于 2012-08-25T22:05:27.003 回答