1

我正在尝试构建一个简单的爬虫,但似乎所有线程都没有完成,即使队列是空的:

#!/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;

那么等待这些线程完成的正确方法是什么?它永远不会跳出那个while循环。

4

2 回答 2

4

$queue->dequeue正在阻塞并等待另一个线程enqueue。从perldoc

从队列的头部删除请求的项目数(默认为 1),并返回它们。如果队列包含的项目数量少于请求的数量,则线程将被阻塞,直到所需数量的项目可用(即,直到其他线程<enqueue> 更多项目)

dequeue_nb()如果队列为空,将返回 undef。但在这种情况下,如果一个线程已将第一个 URL 出列,则其余线程将在任何项目排队之前停止。

在我的脑海中,另一种方法可能是保持当前正在从事某些活动的线程计数,并在达到 0 时终止?

于 2012-11-10T15:34:32.283 回答
1

Thread::Queue 3.01刚刚介绍了这个问题的解决方案。您现在可以声明一个队列已经结束,表明不会再向队列中添加任何项目。这会解除阻塞等待的任何人,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 作者的设计选择。你可能想给他一些反馈,让他知道这会妨碍你。

这是我的原始补丁,它定义done而不是end允许您继续将项目添加到done队列中。

于 2012-11-10T17:16:37.250 回答