我有以下子例程,并让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和事件编程还是比较陌生的,我似乎不能很好地解决我的问题。
谢谢,
发布于 2012-08-26 16:18:47
整个递归的匿名子看起来有点太聪明了,对它自己没有好处。在您的类中创建一个函数,该函数从传入的url (和深度)中获取链接,并将它们添加到对象的数组中。同时创建一个定时器(在=> 0之后),它将元素从数组中移位,如果仍然有元素,则重新启动,否则将end发送到condvar。如果需要,用Thread::Queue对象替换数组。
您还应该只在应用程序代码中调用->recv,而不是在库中调用,或者在condvar上使用回调,而不是调用recv (这将使您能够使用多个condvar并在不依赖线程的情况下向它们发送)
发布于 2012-09-05 00:18:54
这个实用函数的设计错误在于它阻塞了condvar:$cv->recv。这会阻止该函数的用户在全局异步程序中使用它。
相反,get_urls应该返回condvar ($cv),并让用户使用它做一些有用的事情。如果用户想要阻止,他将能够这样做。如果他不这样做,他将可以自由地与其他异步任务共享资源。
您的程序的另一个问题是,如果该函数设计正确,您可能不需要线程:如果该函数是程序的核心,那么它显然是受网络限制的,而不是受CPU限制的,因此您应该能够启动对(固定的) get_urls的多个调用,这些调用将在单个线程中并行执行。
发布于 2012-08-26 06:05:27
您的回调函数中包含$cv->end;。在我看来这是错误的,应该是在AnyEvent::HTTP::http_get调用之后,AFAIU。
https://stackoverflow.com/questions/12124068
复制相似问题