什么是等待Thread :: Queue完成的正确方法?

时间:2012-11-10 15:15:14

标签: perl loops

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

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

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

2 个答案:

答案 0 :(得分:4)

您的$queue->dequeue正在阻止并等待另一个线程转到enqueue。来自perldoc

  

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

如果队列为空,

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

关闭顶部,另一种方法可能是保留当前参与某些活动的线程数,并在达到0时终止?

答案 1 :(得分:1)

Thread::Queue 3.01刚刚介绍了solution to this problem。您现在可以声明队列已结束,表明不再有任何项目添加到队列中。这将取消阻止等待dequeuedequeue的任何人在队列为空时不会阻止您的线程退出。

$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队列。