Почему мой скрипт не обрабатывает все элементы в массиве? - PullRequest
0 голосов
/ 24 сентября 2010

Следующий код является тестом для проверки того, что я уже сделал с моим новым найденным набором потоков.

#!/usr/bin/perl
use strict;
use warnings;
use threads;
use threads::shared;
use URI;
use URI::http;
use File::Basename;
use DBI;
use HTML::Parser;
use LWP::Simple;
require LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->timeout(10);
$ua->env_proxy;
$ua->max_redirect(0);

print "Starting main program\n";

my @urls = ('http://www.actwebdesigns.co.uk', 'http://www.1st4pets.com', 'http://www.special4you.com');
my @threads;
while ( @urls ) {
        my $url = shift ( @urls );
        my $t = threads->new(\&scan, $url);
        push(@threads,$t);
}
while (@threads) {
        my $url_thread = shift(@threads)->join;
}
sub resolve_href {
    my ($base, $href) = @_;
    my $u = URI->new_abs($href, $base);
    return $u->canonical;   
}
sub redirect_test {
    my $url = shift;
    my $redirect_limit = 10;
    my $y = 0;
    my( $response, $responseCode );
    while( 1 && $y le $redirect_limit ) {
        $response = $ua->get($url);
        $responseCode = $response->code;
        if( $responseCode == 200 || $responseCode == 301 || $responseCode == 302 ) {
            if( $responseCode == 301 || $responseCode == 302 ) {
                $url = resolve_href( $url, $response->header('Location') );
            }else{
                last;
            }
        }else{
            last;
        }
        $y++;
    }
    return ($url, $response, $responseCode, $redirect_limit, $y );
}
sub scan {
        my $url = shift;
        my @hrefs_found;
        print "started scanning: $url\n";
        my $info = URI::http->new($url);
        # if url is not an absolute url
        if( ! defined( $info->host ) ) {
            print "Invalid URL: $url \n";    
        }else{
             my $host = $info->host;
            $host =~ s/^www\.//;
            # check to see if url is valid, checks for redirects (max of 10)
            my @urlI = redirect_test( $url );
            my $content = '';
            # checks to see if url did not redirect more than 10 times and that response returned was 200
            if( $urlI[4] != $urlI[3] && $urlI[2] == 200 ) { 
                $content = $urlI[1]->content;
                die "get failed: " . $urlI[0] if ( ! defined $content );
            }
            # sticks all hrefs on a page in an array
            my @pageLinksArray = ( $content =~ m/href=["']([^"']*)["']/g );
            # foreach links found
            foreach( @pageLinksArray ) {
                # make href an absolute url
                my $url_found = resolve_href( $urlI[0], $_ );
                # check if url looks like a valid url
                if( $url_found =~ m/^http:\/\// ) {
                    my $info = URI::http->new($url_found);
                    # check to see if url is a valid url
                    if( ! defined( $info->host ) ) {
                        print "Invalid URL: $url_found \n";    
                    }else{
                        my %values_index;
                        @values_index{@hrefs_found} = ();
                        my %values_index2;
                        @values_index2{@urls} = ();
                        # if url is not already been found
                        if( ! exists $values_index{$url_found} && ! exists $values_index2{$url_found} ) {
                            # add to arrays
                            push( @hrefs_found, $url_found );
                            push( @urls, $url_found );
                        }
                    }
                }
            }
            print "$url found " . scalar @hrefs_found . "\n";

        }
        return $url;
}

Проблема в том, что ближе к концу скрипта новые найденные URL-адреса добавляются в массивы, но код в верхней части скрипта не обрабатывает их, т. Е. Проходит только первые тестовые URL-адреса.

Кто-нибудь может понять, почему это происходит?

С уважением,

Phil

РЕДАКТИРОВАТЬ **

Я пытался приостановить это, делая что-то вроде этого:

while ( @urls ) {
my $url = shift ( @urls );
my $t = threads->new(\&scan, $url);
push(@threads,$t);
my $n = 0;
while( 1 ) {
    if( scalar @urls == 1 ) {
        sleep 10;
    }else{
        last;
    }
    if( $n >= 1 ) {
        print "IN ARRAY URLS:\n\n";
        print @urls;
        print "\n\n";
        die "Process taking too long.";
        last;
    }
    $n++;
}

}

Но, похоже, он ничего не делает.

результат:

Starting main program
started scanning: http://www.actwebdesigns.co.uk
started scanning: http://www.1st4pets.com
http://www.actwebdesigns.co.uk found 24
http://www.1st4pets.com found 17
IN ARRAY URLS:

http://www.stackoverflow.com

Process taking too long. at C:\perlscripts\thread.pl line 38.
Perl exited with active threads:
        0 running and unjoined
        2 finished and unjoined
        0 running and detached

Ответы [ 2 ]

3 голосов
/ 24 сентября 2010

Из того, что я вижу, вы запускаете поток, чтобы получить каждый URL в исходном списке, просмотреть его и добавить найденные URL в исходный список.

Проблема в том, что все это происходити сопоставление занимает некоторое время, и цикл, который запускает потоки, вероятно, будет выполнен задолго до того, как будут добавлены первые новые URL.После этого он больше не просматривает список, поэтому новые URL-адреса не будут обрабатываться.

Для справки, вам действительно нужна синхронизация и сигнализация.Большинство языков делают это, используя мьютексы, «условия» или семафоры.Пока вы делаете что-то подобное, вам, в основном, придется запускать цикл while снова и снова после присоединения к каждой партии потоков из предыдущего цикла while.

На самом деле ...

Просматривая документы, я нахожу this :

Начиная с 5.6.0, Perl поддерживаетновый тип потоков, называемый потоками интерпретатора (ithreads).Эти потоки могут использоваться явно и неявно.

Ithreads работает путем клонирования дерева данных, чтобы данные не передавались между разными потоками.

Хорошие новости / плохие новости.Хорошей новостью является то, что вам не нужно беспокоиться о поточно-ориентированном доступе к @urls, как он появился впервые.Причиной тому являются плохие новости: каждый поток имеет свой @urls, поэтому вы не можете таким образом обмениваться данными без какой-либо дополнительной помощи.

Вместо этого вы, вероятно, захотите сделать следующее:создайте поток в контексте списка, и пусть он возвращает список найденных URL-адресов, которые затем можно добавить к @urls, когда вы join поток.Альтернатива (совместное использование @urls между потоками) может быть ужасно быстрой, если вы не знаете о проблемах безопасности потоков.

Однако, если вы сделаете это, сценарий поглотит огромное количестворесурсы - только три тестовых URL содержали 42 других URL-адреса, и многие из них, вероятно, имеют собственные URL-адреса.Поэтому, если вы собираетесь запускать один поток на запрос, вы очень быстро создадите больше потоков, чем может справиться практически любая машина.

1 голос
/ 24 сентября 2010

По умолчанию каждый поток имеет свою собственную личную копию данных. То есть, когда вы добавляете новые элементы в @urls в одном потоке, копия @urls во всех остальных потоках не обновляется, включая копию в «родительском» потоке / процессе.

Когда вы будете готовы открыть еще одну банку с червями, проверьте модуль threads::shared, который обеспечивает неуклюжий, но полезный способ обмена данными между потоками.

...