Модераторы: korob2001, ginnie
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Помогите плз с многопоточностью 
:(
    Опции темы
infodime
Дата 30.9.2006, 05:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 30.9.2006

Репутация: нет
Всего: нет



Помогите плз реализовать такое многопоточное приложение под линуксом:
Нужно фетчить и обрабатывать много разных страниц с помощью LWP во много потоков, и чтобы все работало максимально быстро.
Я думаю сделать с помощью форков.

С LWP проблем нет, но не пойму, как отследить чтобы было запущено одновременно например 100 детей и не больше?
Пока приходит мысль только смотреть, скажем, раз в 5 секунд таблицу процессов, сколько запущено форков.
Но это ведь будет тормозить? Да и не красиво как-то.

Или может лучше будет реализовать это с помощью threads?
В поиске не нашел smile
Спасибо.
PM MAIL   Вверх
Usya
Дата 1.10.2006, 21:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 154
Регистрация: 7.6.2005

Репутация: нет
Всего: нет



А тебе админ разрешит запускать сто процессов одновременно?

Если да, то можно поступить следующим образом: 
1) Каждый потомок будет создавать временный файл с каким-либо именем (главное, чтобы не было повторений) и удалять после своей работы, а родитель каждые пять секунд (можно реализовать используя, например,  sleep(5)) отслеживать сколько файлов существует и если не хватает, то создавать новых потомков.
2) У родителя создать массив с номерами соответствующих процессов. Обход процессов по аналогии каждые 5 секунд. Единственное, могут быть некоторые накладки - потомка уже нет, а процесс с соответствующим номером существует в связи с запуском другой проги.

Цитата

Или может лучше будет реализовать это с помощью threads?

На счет этого тоже не мешало бы уточнить - есть ли такая возможность на серваке?
--------------------
Я не волшебник, я только учусь...
PM MAIL   Вверх
Nab
Дата 1.10.2006, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 582
Регистрация: 25.3.2006
Где: Kiev

Репутация: 26
Всего: 37



На CPAN в каталоге с LWP лежит вот такой модуль 
ParallelUserAgent

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

Посему на днях буду ковырять этот и другие Spiders, есть еще разные на CPAN...


--------------------
 Чтобы правильно задать вопрос нужно знать больше половины ответа...
Perl Community 
FREESCO in Ukraine 
PM MAIL   Вверх
JUmPER
Дата 2.10.2006, 08:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 196
Регистрация: 22.8.2006

Репутация: нет
Всего: 3



а вот если ParallelUserAgent не понравится, то можно же просто потоки юзать...
вместо процессов -- это
1 экономичнее
2 удобнее
3 и админы орать не будут
--------------------
Существует 10 типов людей: те, которые понимают двоичную систему, и те, которые ее не понимаютСуществует 10 типов людей: те, кто понимают троичную систему, те, кто ее не понимают и те, кто путает ее с двоичной
PM MAIL   Вверх
Ezh
Дата 4.10.2006, 12:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 21
Регистрация: 11.9.2006

Репутация: нет
Всего: нет



тема старая - юзай POE и будет тебе счастье (POE::Wheel::Run), а с потоками связываться не советую, очень там еще много "сюрпризов", особенно под большой нагрузкой
PM MAIL   Вверх
Buhalich
Дата 11.10.2006, 14:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 28
Регистрация: 14.9.2006

Репутация: нет
Всего: нет



Цитата(infodime @  30.9.2006,  05:56 Найти цитируемый пост)
Я думаю сделать с помощью форков.

Лучше use threads;
Цитата(infodime @  30.9.2006,  05:56 Найти цитируемый пост)
В поиске не нашел smile

=) в консольке набери 
Код
perldoc threads


PM MAIL   Вверх
alexu_id
Дата 12.10.2006, 07:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 15
Регистрация: 5.10.2006

Репутация: нет
Всего: нет



согласен с Ezh, поскольку сам только что занимался этой самой многопоточностью. на практике через POE http://poe.perl.org работает быстрее и нагрузка на сервер меньше. Правда не могу понять как это происходит smile поскольку в анотации к POE они заявляют что форки вообще не используют для сових сессий, да и в таблице процессов не видно чтобы процесс делился.


как альтернативный вариант POE:

 для форков можно создать глобальный hash который содержит потомков и в цикле отслеживать его размер, а контролировать умирающих потомков в hash через сигнал

#! /usr/bin/perl

use POSIX qw( :signal_h :errno_h :sys_wait_h );
use LWP::Simple;

my $TIMEOUT  = 300; # задержка
my $MAX_THREADS = 100; # максимальное количество потоков

my %CHILDPIDS = (); # потомки
$SIG{ CHLD } = \&REAPER; # сигнал когда потомок кирдык

my @urls= (); # список urls

# основной цикл
while( ) {

   for( my $t = 0; $t <= $#urls;  $t ++ ) {
      my $url = $urls[$t];

      if( my $pid = fork ) {
         $CHILDPIDS{ $pid } = $pid if( defined $pid );
      } else {
              my $content = $content = get($url);

             # ну и тут то что надо дальше

      }
      while( $#{[ keys %CHILDPIDS ]} + 1 == $MAX_THREADS ){}
   }
   sleep( $TIMEOUT ); # задержка
}


exit(0);

sub REAPER{
   while( my $kid = waitpid( -1, WNOHANG ) ){
      last if $kid == -1;
      if( WIFEXITED( $? ) ){
         delete( $CHILDPIDS{ $kid } );
      }
   }
   $SIG{ CHLD } = \&REAPER;
}


Это сообщение отредактировал(а) alexu_id - 12.10.2006, 07:54
PM MAIL   Вверх
infodime
Дата 12.10.2006, 16:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 30.9.2006

Репутация: нет
Всего: нет



alexu_id спасибо за пример, но че-то он не работает

Через какое-то время все форки отрабатывают, а новые перестают создаваться
Вроде как отработанные не удаляются из хеша.

PM MAIL   Вверх
Ezh
Дата 13.10.2006, 09:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 21
Регистрация: 11.9.2006

Репутация: нет
Всего: нет



POE прост и отлажен
Он стар как ... мамонта, там уже ошибок-то, наверное, не осталось

Кусок из моего кода
Код

POE::Session->create(
    inline_states => {
        _start          => \&transformerStart,
        iteration       => \&transformerLoop,
        worker_stdout   => \&transformerResult,
        worker_stderr   => \&transformerError,
        worker_done     => \&transformerDone,
        catch_sigchld => sub {
      my ($kernel, $heap) = @_[KERNEL, HEAP];

      # Count the child reap.
      $heap->{reaped}++;

      # Refresh the fork timeout.
      $kernel->delay(1);
#        reaping_time_is_up => 2 * ($heap->{forked} - $heap->{reaped} + 1)
    },
    },
#    options => { trace => 1, debug => 1 }
);

sub transformerStart {
    my $kernel = $_[KERNEL];
    $kernel->alias_set('transformer');
    $kernel->delay_add("iteration", 0.001);
}

# spool - hash file/profile/(start, end, messages(code,message), result, id)
sub transformerLoop {
    my $kernel = $_[KERNEL];
    my $heap = $_[HEAP];

    if (keys(%{$heap->{task}})<2) {
        my $next_task = shift(@transform_queue);
        if(defined($next_task)) {
            my $time =  $next_task->[0];
            my $anchor = $next_task->[1];
            my $key =  $next_task->[2];
            my $params = $next_task->[3];
            my $routine;
            
            if (exists($config->{profiles}{$anchor})) {
                $logger->logwarn('Start task for '.$anchor.': '.$params->[1]);
                taskBeginTime($heap, $anchor, $key, $time);
                my $task = POE::Wheel::Run->new(
                    Program => \&worker,
                    ProgramArgs => [$anchor, $key, $params],
                    StdoutFilter => POE::Filter::Reference->new(),
                    StdoutEvent  => "worker_stdout",
                    StderrFilter => POE::Filter::Reference->new(),
                    StderrEvent  => "worker_stderr",
                    CloseEvent   => "worker_done");
                $heap->{task}->{$task->ID} = [$task, $anchor, $key, $params];
                taskID($heap, $anchor, $key, $task->ID);
            } else {
                warn('ERROR - UNKNOWN ROUTINE: ');
            };
        };
    };
    $kernel->delay_add("iteration", 0.01);
}


Лимит по форкам это keys(%{$heap->{task}})<2

Если что не понятно, то советую юзать perl -d и $DB::single = 1 - это все лучше меня объяснит smile 
PM MAIL   Вверх
infodime
Дата 15.10.2006, 04:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 30.9.2006

Репутация: нет
Всего: нет



Ezh, спасибо большое! Разобрался с POE - все заработало как надо, очень удобная вещь smile

Есть несколько непонятых моментов, проясните плз smile

Мне нужно сделать так: стартуется первая функция, в ней делается выборка из базы, скажем на 2000 урлов. По аналогии с твоим кодом, пусть эта функция называется transformerLoop1
Из нее вызывается другая функция, transformerLoop2, в которой обрабатывается в многопоточном режиме (форками, с помощью POE::Wheel::Run) каждый урл из этого массива, делается shift из массива, и когда в нем остается 0 элементов, вызывается опять transformerLoop1 где делается выборка на следующие 2000 урлов
Вопрос: как правильно организовать обмен переменными? Чтобы из массива, который был создан в функции transformerLoop1 правильно удалялись элементы (shift) всеми форками, которые порождаются функцией transformerLoop2?

Я сделал с помощью $heap, но правильно ли это?

И второй вопрос.
Если выборку делать из mysql в функции transformerLoop1 с помощью обычного DBI то он на второй выборке отваливается с фразой Mysql server has gone away, хотя дисконнект я не вызывал.

Покопавшись в гугле, сделал с помощью EasyDBI и SimpleDBI
А как правильней? smile

Спасибо
PM MAIL   Вверх
Ezh
Дата 15.10.2006, 12:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 21
Регистрация: 11.9.2006

Репутация: нет
Всего: нет



У тебя что-то  с архитектурой... В твоем варианте я вряд ли что то посоветую
Я бы сделал так: есть два набора форкающихся объектов
1 набор - запрос 2000 урл из базы и заливка результатов в пул
2 набор - обработка уже запрошенных урл в пуле
синхронизация работы наборов объектов и хранение пула все с помощью основного процесса
то есть набор 1 обращается к родителю и набор 2 обращается к родителю, но напрямую никогда 1 и 2 не взаимодействуют

ЗЫ IMHO есть heap объекта и есть heap родителя - не стоит их мешать
ЗЗЫ 1ый набор ждет пустого пула, если пул родителя пуст, то заливает туда очередную порцию данных

по факту IMHO должно получиться то, что ты хочешь

DBI не совсем fork safe smile если ты форкаешь дескриптор, то когда его закрывает потомок, сессия закрывается и у родителя. Если не нравится такая ситуевина юзай InactiveDestroy - что это такое смотри в описании DBI, специально для таких случаев сделали

Добавлено @ 12:32 
естественно 1ый набор будет состоять только из 1ого процесса  smile 
PM MAIL   Вверх
infodime
Дата 20.10.2006, 00:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 30.9.2006

Репутация: нет
Всего: нет



Ezh, спасибо, сделал, как ты посоветовал - все ок smile

Возникла другая проблема, не связанная с этим

На одном компе почему-то остаются зомби. Т.е. процесс, который тянет и парсит урлы родился, отработал, умер, а зомби остался. Через какое-то время таблица процессов переполняется и демон умирает. Это все происходит на FedoraCore последней, Xeon 1гб памяти
Что интересно, на другом компе зомби не создаются.. там - gentoo и athlon xp 1gb памяти.
Не знаешь, в какую сторону копать? И вообще, как отлавливать, почему эти зомби появляются?

И второй вопрос: Ставлю максимальное число детей в 150, смотрю таблицу процессов - число все время колеблется в пределах 60-110. Память при этом не вся расходуется
А если плодить процессы, которые выполняют только sleep(60) - то создается ровно 150 процессов.

Из-за чего не досоздаются процессы?
PM MAIL   Вверх
Ezh
Дата 21.10.2006, 17:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 21
Регистрация: 11.9.2006

Репутация: нет
Всего: нет



вообще зомби это процесс, который помер, но не похоронен smile (красиво звучит) то есть твой родительский процесс почему-то не обработал сигнал SIG_CHILD

Ниже кусок из changes POE, проверь версию на FC, если старая - проапгрейдь до 0.38
sig() reference count rolled back.

sig_child() to wait on a particular PID (no more checking every  
SIGCHLD's PID for validity).  Plus it holds a reference count.  And  
it cancels itself once the interesting PID has been delivered.

Maybe some other stuff, but those are the big ones.  No other changes  
are planned before 0.38, but some minor patches might sneak in.   
Expect it on CPAN early this week.

Кроме того, можешь в код родителя вставить принудительную чистку в цикле - пока есть живые мертвецы ($pid>-1) - хоронить
и пускать ее перед каждым вызовом нового процесса (конечно это не особо красиво) Что-то типа
Код

$pid = waitpid(-1, &WNOHANG);
if ($pid == -1) {
     # no child waiting.  Ignore it.
} elsif (WIFEXITED($?)) {
      print "Process $pid exited.\n";
} else {
    print "False alarm on $pid.\n";
}


По второму вопросу, не уверен, но сам с таким тоже встречался. Мои размышления, могу быть не прав. Т.к. проца у тебя только 2ва а потоков много больше, то при росте количества потоков время User CPU стремится к нулю, а время System CPU к бесконечности, т.к. любой форк это тоже ресурсоемкая операция, так что если ты сделаешь максимум 10 процессов, то у тебя будет 9 работать, сделаешь 10^6 процессов и у тебя все ресурсы уйдут на форки. Именно поетому при компиляции рекомендуют пускать N+1 процессов, где N количество CPU. IMHO ты смело можешь понизить кол-во потоков до 100 и производительность парсинга либо останется та же, либо увеличится, но пренебрежительно мало.
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl"
korob2001
sharq
  • В этом разделе обсуждаются общие вопросы по языку Perl
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Если ваш вопрос относится к CGI программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", можно скачать здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Perl: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1105 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.