我正在尝试构建一个简单的爬虫,但似乎所有线程都没有完成,即使队列是空的:
#!/usr/bin/perl
use strict;
use warnings;
use threads;
use Thread::Queue;
use LWP::UserAgent;
use HTML::LinkExtor;
my $ua = new LWP::UserAgent;
my %visited = ();
my $workQueue = new Thread::Queue;
sub work {
my ($ua, $queue, $hashref) = @_;
my $tid = threads->self->tid;
my $linkExtor = new HTML::LinkExtor;
while (my $next = $queue->dequeue)
{
print "Processin ($tid): ", $next, "\n";
my $resp = $ua->get ($next);
if ($resp->is_success)
{
$linkExtor->parse ($resp->content);
my @links = map { my($tag, %attrs) = @$_;
($tag eq 'a')
? $attrs{href} : () } $linkExtor->links;
$queue->enqueue (@links);
}
}
};
$workQueue->enqueue ('http://localhost');
my @threads = map { threads->create (\&work, $ua, $workQueue, \%visited) } 1..10;
$_->join for @threads;
那么等待这些线程完成的正确方法是什么?它永远不会跳出那个循环。
答案 0 :(得分:4)
您的$queue->dequeue
正在阻止并等待另一个线程转到enqueue
。来自perldoc:
如果队列为空,从队列的头部删除请求的项目数(默认为1),并返回它们。如果队列包含的项目少于所请求的项目数,则线程将被阻止,直到所需的项目数量(即,直到其他线程< enqueue>更多项目)。
dequeue_nb()
将返回undef。但在这种情况下,如果一个线程已将第一个URL出列,则其余线程将在任何项目排队之前停止。
关闭顶部,另一种方法可能是保留当前参与某些活动的线程数,并在达到0时终止?
答案 1 :(得分:1)
Thread::Queue 3.01刚刚介绍了solution to this problem。您现在可以声明队列已结束,表明不再有任何项目添加到队列中。这将取消阻止等待dequeue
和dequeue
的任何人在队列为空时不会阻止您的线程退出。
$workQueue->enqueue('http://localhost');
my @threads = map { threads->create (\&work, $ua, $workQueue, \%visited) } 1..10;
$workQueue->end;
$_->join for @threads;
不幸的是,结束队列也意味着您不能再向队列添加项目,因此在抓取网页中间的线程无法将找到的页面添加到队列中。我编写了原始的Thread :: Queue补丁,它没有这个限制。没有技术原因为什么结束队列不能占用更多项目,限制是Thread :: Queue作者的设计选择。你可能想要give him some feedback让他知道它会妨碍你。
以下my original patch定义done
而不是end
,并允许您继续将项目添加到done
队列。