Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: разработка для Web > Многопоточность LWP как?


Автор: Riddick 25.6.2007, 21:15
Прочитал про форки, но никак не пойму как применить. Понятно что многое об этом написано, но никак не дойдет до меня. Если не трудно, прошу Уважаемых Гуру помочь, новичку самоучке smile .
Из кода ниже станет ясен алгоритм выполнения скрипта, а вот как сделать его многопоточным надеюсь найти ответ здесь на форуме.
Код

#открываю исходный файл, каждая строчка которого содержит определенный параметр

     open (CC, '<temp/temp1.txt');
          @col=<CC>;
             $col=@col;
     close (CC);

#Запускаю цикл построчного чтения файла. Каждая строка является запросом (грубо говоря урлом)

$i=1;
     while ($i <= $col){

#в нижеследующией переменной содержится ответ сервера, а точнее html страница

     my $html = Geturll($url);

#здесь идет блок получения вышеуказанной переменной $html

sub Geturll {
    my $url = shift;
    my $ua = LWP::UserAgent->new;
    my $response = $ua->get($url);
    if ($response->is_success and $response->status_line =~ /200/ ) {
        return $response->content;
    } else {
        Geturl($url);
    }
}

#Дальше идет обработка полученной переменной $html


foreach (split /\n/, $html) {

 if ($html=~ m/\&nbsp\;(\d+)/g)
 {$chislo = $1;

if ($chislo < $miniz){last();}

}

if ($html=~ m/color=#000000>([A-Za-z\s\d\.]+)/g)
 {$dannye = $1;

if ($dannye eq ""){last();}

}
#Запись полученных данных в файл
open (CC, 'text.txt');
print CC "$dannye\n";
close (CC);

}
$i++;
}

#Ну и дальше сообщение что все закончилось

print <<EOF;

Поздравляю, ты наконец то дождался!

EOF



Из кода понятно, что скрипт по очереди выбирает по одному запросу из фала и обрабатывает и ждать приходится "от забора и до обеда", а вот если бы он это делал многопоточно, ну хотябы в 30 или 50, то время ожидание сократилось бы в разы. Буду очень благодарен за помощь.
З.Ы. Сильно не бейте, если очень туплю!

Автор: Ivan Kolesnikov 26.6.2007, 06:54
В Perl существует, по крайней мере, два способа организации многопоточности, это fork и собственно потоки.

Я работал только с fork, поэтому расскажу про него: когда ты делаешь системный вызов fork, то создается копия твоего процесса, при этом родительскому процессу возвращается pid только что созданного нового процесса, а дочернему процессу - ноль.

В твоем случае можно многопоточность реализовать следующим образом:
Код

#открываю исходный файл, каждая строчка которого содержит определенный параметр

open (CC, '<temp/temp1.txt');
@col=<CC>;
$col=@col;
close (CC);

#Число потоков
$COUNT_THREAD = 20;

#Создаем потоки

for ($i = 0; $i<$COUNT_THREAD; $i++) {
    $pid = fork();
    if ($pid) {
        #Это родитель
        #Будем сохранять pid-ы потоков, чтобы потом дождаться их всех завершения
        push @pids, $pid;
    } else {
        #А это уже новый поток
        for ($j = $i; $j<$col; $j+=$COUNT_THREAD) {
            my $html = Geturll($url);
            foreach (split /\n/, $html) {
                if ($html=~ m/\&nbsp\;(\d+)/g) {
                    $chislo = $1;
                    if ($chislo < $miniz){
                        last();
                    }
            }
            if ($html=~ m/color=#000000>([A-Za-z\s\d\.]+)/g) {
                $dannye = $1;
                if ($dannye eq ""){
                    last();
                }
            }
            #Запись полученных данных в файл
            open (CC, 'text.txt');
            print CC "$dannye\n";
            close (CC);
        };
    };
};

#Потоки запущены ждем их завершения
waitpid($_) foreach @pids;

#Ну и дальше сообщение что все закончилось

print <<EOF;

Поздравляю, ты наконец то дождался!

eof

#здесь идет блок получения вышеуказанной переменной $html

sub Geturll {
    my $url = shift;
    my $ua = LWP::UserAgent->new;
    my $response = $ua->get($url);
    if ($response->is_success and $response->status_line =~ /200/ ) {
        return $response->content;
    } else {
        Geturl($url);
    }
}


Я чуток подправил код, чтобы проще было читать, да я его не проверял, так что могут быть небольшие ошибки.

Но суть в следующем основной поток создает необходимое число потоков, каждый из которых знает какие данные ему необходимо обработать (он обрабатывает не все подрят, а скачет (величина прыжка равна количеству потоков). При этом основной поток сохраняет идентификаторы своих детей, чтобы в конце подождать пока они все завершаться...

Если что не понятно пиши...

Автор: dimes 26.6.2007, 08:56
Riddick, 
Очень много ошибок в коде.


Код

open (CC, '<temp/temp1.txt');
@col=<CC>;
$col=@col;
close (CC);


Каждый элемент @col будут заканчиваться "\n" а то и "\r\n"
Это может быть принципиально.

насчет этого: $col=@col;
В perl много способов как пройтись по массиву
Код

for( my $_i=0;$_i<@col;$_i++ ){
 my $_url=$col[$_i];



Код

for( my $_i=0;$_i<=$#col;$_i++ ){
 my $_url=$col[$_i];



Код

for( @col ){
 my $_url=$_;


и т.д.

Функция "Geturll" объявляется прямо в цикле, это все равно что грязными руками лапать прекрасное и вечное  smile

Код

#Запись полученных данных в файл
open (CC, 'text.txt');
print CC "$dannye\n";
close (CC);

-Это не запись а чтение
-не хватает обработчика ошибок open (CC, 'text.txt') or &my_prc_err($!);
-нет flock (в примере Ivan Kolesnikov это особенно важно)

НЕТ "use strict;"
пока не научился вставлять
use strict;
use CGI::Carp qw (fatalsToBrowser);
ты далеко не продвинешься.
еще бы хорошо -w (use warning;)

Код

if ($response->is_success and $response->status_line =~ /200/ ) {
 return $response->content;
} else {
  Geturl($url);
}

Если на той стороне что то мега ляжет или изменится у тебя зависнут скрипты, бесконечные рекурсии не самое правильно решение особенно у тебя  smile 

Автор: amg 26.6.2007, 10:09
Riddick, вот этот кусок твоего кода, боюсь, делает не (совсем) то, что ты хочешь. Можно долго рассказывать, что в нем нехорошо, но проще поправить. Только я, к сожалению, не понял, чего нужно добиться.
Код

foreach (split /\n/, $html) {
  if ($html=~ m/\&nbsp\;(\d+)/g) {
    $chislo = $1;
    if ($chislo < $miniz) {last();}
  }
  if ($html=~ m/color=#000000>([A-Za-z\s\d\.]+)/g) {
    $dannye = $1;
    if ($dannye eq "") {last();}
  }
  open (CC, 'text.txt');
  print CC "$dannye\n";
  close (CC);
}

Автор: JAPH 26.6.2007, 11:12
Где-то я натыкался на то, что нельзя давать детям выходить обратно в родительский код. То есть после кода, выделенного amg, не нужен exit? И, кстати, eof в нижнем регистре после <<EOF - опечатка?

Автор: Ivan Kolesnikov 26.6.2007, 11:16
Цитата

Где-то я натыкался на то, что нельзя давать детям выходить обратно в родительский код. То есть после кода, выделенного amg, не нужен exit? И, кстати, eof в нижнем регистре после <<EOF - опечатка? 

да конечно нужен exit и на счет eof - опечатка. Просто писал сразу в браузере и не проверял работу...

Автор: Riddick 26.6.2007, 17:29
Большое спасибо всем, все учту и приму к сведению. Насчет ошибок в коде, тоже спасибо, хотя вроде копипастом вставлял а потом правил, поэтому и ошибки. Насчет use strict - говорили же мне сразу привыкать сразу его юзать, теперь точно начну, насчет фатал браузер и варнингов вставляю в каждый скрипт.
Еще раз всем спасибо, если возникнут вопросы, хоть знаю куда теперь обратиться. Всем респект.

Автор: Riddick 26.6.2007, 18:29
Попробовал, браузер выдал кучу ошибок
Not enough arguments for waitpid at d:\cgi-bin\PARSER~1\parser2.cgi line 72, near "$_) "

Missing right curly or square bracket at d:\cgi-bin\PARSER~1\parser2.cgi line 83, at end of line
  (Might be a runaway multi-line << string starting on line 77)

Ну и еще, сечас проверяю, может опять я ступил

Автор: igor_k75 27.6.2007, 22:09
Можно обойтись и без fork.

LWP::Parallel - http://search.cpan.org/~marclang/ParallelUserAgent-2.57/lib/LWP/Parallel.pm

Автор: nitr 27.6.2007, 22:47
igor_k75, вы сразу приведите пример его использования smile Поймёте, что многим людям проще с форками работать, или (что сам бы посоветовал) с POE разобраться!

;)

Автор: Riddick 28.6.2007, 05:07
Если не секрет, что такое РОЕ и где про него почитать. А так сделал на форках, в 20 потоков проц грузится на 98%, может из-за того, что Апачи стоит на Windows, потому как прочитал, форки это линуксовая стихия.

Автор: sharq 28.6.2007, 15:27
Riddick, 

http://search.cpan.org/%7Ercaputo/POE/
http://poe.perl.org/
http://perl.wikia.com/wiki/POE

На русском информации очень мало, пока это только обсуждения здесь на форуме и в http://blog.yandex.ru/search.xml?text=perl+POE.

 smile 

Автор: Riddick 2.7.2007, 22:21
Опять вопрос, для тех кто работает с форками и на денвере. Можно ли как нибудь уменьшить нагрузку на проц при применении Forkов, если можно напишите плиз, если нет то может можно реализовать как нибудь по другому многопоточность на стандартных модулях. Заранее спасибо!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)