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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Многопоточность, через fork 
:(
    Опции темы
Bulat
Дата 22.8.2007, 17:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



Вот пришлось создать грамотное многопоточное приложение. Почти весь день убил, неоднократно пытался найти что-то с форума что могло бы помочь, в итоге собрал по кусочкам, ИМХО не было ни одной темы, которая давала ответы на все вопросы поставленные мною для решения задачи.

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

Вот в итоге цельный небольшой пример, для тех кто в будущем будут сталкиваться с подобными задачами. Чтоб не пришлось как и мне искать и собирать все по кускам smile 
Код

#!/usr/bin/perl

use strict;
use POSIX 'WNOHANG';

#начальное значение количества потоков
my $forks = 0;
#максимальное значение количества потоков
my $maxForks = 2;

#логируем на выходе для более или менее наглядного представления как все отработало
open (LOG, ">log");

#переменная - индекс обрабатываемого данного из списка
my $i = -1;

#цикл пока не обработаются все данные из списка, допустим их 6, далее будет понятно почему :)
while ($i < 7) {
    #проверяем количество уже работающих дочерних процессов
    if ($forks <= $maxForks) {
        #для каждого нового дочернего процесса, индекс очередных данных из списка
        $i++;
        # увеличиваем индекс запущенных дочерних процессов
        $forks++;

        #кусок кода для дочернего процесса
        if (fork == 0) {
            #получили id родительского процесса, у меня иногда почему-то возвращает 1, так и не понял почему 
            my $ppid = getppid();
            #процедура, в которую передаются данные уникальные для каждого дочернего процесса, 
            #и в которой эти данные соотв обрабатываются
            MyProc("child", $ppid, $i, $forks);
            #дочерний процесс стал "зомби"
            exit;
        }
         
        #если есть зомби, окончательно его киляем
        $SIG{CHLD} = sub {
            while ( ( my $kid = waitpid( -1, WNOHANG ) ) > 0 ) {
                warn "Reaped child with PID $kid\n";
                #индекс работающих дочерних процессов уменьшаем
                $forks--;
            }
        };
    }

    #аналогично, вообще на всякий случай пихнул и туда и сюда, наверху если дочерний процесс закончил работу
    #если мы еще не набрали количество максимальных дочерних процессов
    #здесь если у нас уже работают максимальное количество дочерних процессов, но еще не все данные обработаны
    $SIG{CHLD} = sub {
        while ( ( my $kid = waitpid( -1, WNOHANG ) ) > 0 ) {
            warn "Reaped child with PID $kid\n";
            $forks--;
        }
    };

}

close (LOG);

sub MyProc {
    my ($value, $ppid, $i, $forks) = @_;
    #маленькая проверка на многопоточность, если дочерние процессы действительно работают параллельно, то 
    #строка где $i=3 будет после строки, где $i = 7
    if ($i == 3) {
        sleep(5);
    }
    #запишем в лог, данные от каждого дочернего процесса
    #если повезет, то иногда бывает, что строка, допустим где $i = 5, запишется раньше, чем строка где $i = 4 -
    #что тоже подтверждает что процессы работают параллельно, но вообще как правило, данные будут писаться в лог
    #по возрастанию индекса $i
    print LOG ("$value - $ppid - $i - $forks\n");

    #можно убрать все что выше и писать ваш код, конечно не забыв передать ему нужные индексы, данные и т.п.
}


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


--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
Nab
Дата 22.8.2007, 17:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



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

Скриптик проверки проксей на работоспособность, это чтоб коментарии были понятны smile
Прошел проверку временем, ибо до сих пор стоит и работает, и каждые пол часа выдает список рабочих проксей. smile

Код

# количество параллельных процессов проверки
# используеться Getopt::Std...
my $child_count = $opts{'f'} || 20;
$child_count = 1 if $child_count <= 0;

# вывод отладочной инфы в лог ошибок.
sub debug_log {    print STDERR "[ ". scalar localtime(time) ." ] @_\n" if $debug }

# Ну а здесь начинаем делить на параллельные процессы, а то дюже медленно чекает
# количество прокси на процесс
# минимум 1
my $child_proxy_count = int(@proxy_list / $child_count) || 1;

# если в остатке слишком ного проксей, то распределяем их по процессам.
$child_proxy_count++ if @proxy_list > $child_count and (@proxy_list % $child_count) > $child_proxy_count;
debug_log "--- Set $child_proxy_count for each child  ---";

# этот список будет определять свои прокси для каждого процесса
my @child_proxy_list = ();
# ну а это отстаточные прокси, которые мы прочекаем в главном процессе
my @last_proxy_list = ();

debug_log "--- Forked to max $child_count processes ---";

# пока список проксей больше количества определенного каждому процессу, мы форкаемся
while (@proxy_list >= $child_proxy_count){
    # выделяем прокси
    (@child_proxy_list[0..$child_proxy_count - 1], @proxy_list) = @proxy_list;
    # обнуляем остаток
    @last_proxy_list = ();
    # форкаемся
    if (fork) {
        debug_log "--- Forked for $child_proxy_count proxyes, from $proxy_num to ".($proxy_num + $child_proxy_count)." ---";
        last
    }
    # увеличиваем счетчик для лога
    $proxy_num += $child_proxy_count;
    # устанавливаем остаток проксей
    @last_proxy_list = @proxy_list;
    # и обнуляем дочерний список
    @child_proxy_list = ();
}

####################################################################################
#
# Здесь начинаеться общая часть для детей и головного процесса, сам процесс проверки
# Главное отличие головного процесса от форков это не пустой список @last_proxy_list
#
####################################################################################

# если в остатке что-то есть
# это осается в головном процессе
if (@last_proxy_list) {
    @child_proxy_list = @last_proxy_list;
    debug_log "--- Checked for last ".scalar @child_proxy_list." proxyes, from $proxy_num to ".($proxy_num + scalar @child_proxy_list)." ---"
}

# начинаем проверку
foreach my $proxy (@child_proxy_list) {
....


В итоге скрипт просто завершался когда завершались все дети... с зомби проблем не было... да и неоткуда им было взяться...

Зато главный результат был достигнут, помк проксей ускорился в 15 раз smile


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


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



Цитата(Nab @  22.8.2007,  17:38 Найти цитируемый пост)
у меня было допущение, что обмена данными между детьми и головным процессом не будет


ну это не просто допущение, это условие очень важное, влияет на алгоритм smile

Кстати скриптец очень даже интересный, вот только хотелось бы более уточнить
Цитата(Nab @  22.8.2007,  17:38 Найти цитируемый пост)
проверки проксей на работоспособность


Практически постоянно приходится юзать проксю, возможно данный скрипт мне еще очень здорово пригодится smile



--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
Bulat
Дата 3.9.2007, 19:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



Чуточку переделал код, относительно тех строк, которые киляют "зомби". Этот скрипт будет работать очень хорошо и в таком виде. Дело в том, что в своих скриптах, у меня возникли небольшие проблемы, относительно "долговисящих", дочерних процессов. Я не знаю связано ли это конкретно с обработкой данного сигнала, у меня в скриптах есть обработка не только данного сигнала, но не исключено. Поэтому на всякий случай лучше все же писать так. Как минимум в любом случае одной обработки сигналов достаточно, и лучше эту обработку писать как можне раньше. Практически в самом начале скрипта.

Код

#!/usr/bin/perl

use strict;
use POSIX 'WNOHANG';

#начальное значение количества потоков
my $forks = 0;
#максимальное значение количества потоков
my $maxForks = 2;

#логируем на выходе для более или менее наглядного представления как все отработало
open (LOG, ">log");

#переменная - индекс обрабатываемого данного из списка
my $i = -1;

$SIG{CHLD} = sub {
    while ( ( my $kid = waitpid( -1, WNOHANG ) ) > 0 ) {
        warn "Reaped child with PID $kid\n";
        $forks--;
    }
};


#цикл пока не обработаются все данные из списка, допустим их 6, далее будет понятно почему :)
while ($i < 7) {
    #проверяем количество уже работающих дочерних процессов
    if ($forks <= $maxForks) {
        #для каждого нового дочернего процесса, индекс очередных данных из списка
        $i++;
        # увеличиваем индекс запущенных дочерних процессов
        $forks++;

        #кусок кода для дочернего процесса
        if (fork == 0) {
            #получили id родительского процесса, у меня иногда почему-то возвращает 1, так и не понял почему 
            my $ppid = getppid();
            #процедура, в которую передаются данные уникальные для каждого дочернего процесса, 
            #и в которой эти данные соотв обрабатываются
            MyProc("child", $ppid, $i, $forks);
            #дочерний процесс стал "зомби"
            exit;
        }
         
    }

}

close (LOG);

sub MyProc {
    my ($value, $ppid, $i, $forks) = @_;
    #маленькая проверка на многопоточность, если дочерние процессы действительно работают параллельно, то 
    #строка где $i=3 будет после строки, где $i = 7
    if ($i == 3) {
        sleep(5);
    }
    #запишем в лог, данные от каждого дочернего процесса
    #если повезет, то иногда бывает, что строка, допустим где $i = 5, запишется раньше, чем строка где $i = 4 -
    #что тоже подтверждает что процессы работают параллельно, но вообще как правило, данные будут писаться в лог
    #по возрастанию индекса $i
    print LOG ("$value - $ppid - $i - $forks\n");

    #можно убрать все что выше и писать ваш код, конечно не забыв передать ему нужные индексы, данные и т.п.





--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
fray
Дата 4.9.2007, 09:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Ну по мне это не приемлимая прога, так как она просто создает потомков, и какждму дает следующую ссылку(увеличивая счетчик),  обмена между процессами нет. Поэтому это как-то не интересно smile.
А зачем вообще через сигнал $SIG{CHLD} начинать ждать завершения процесса, может просто установить в родиделе wait() ? 

Это сообщение отредактировал(а) fray - 4.9.2007, 10:08
PM MAIL   Вверх
Bulat
Дата 4.9.2007, 11:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



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

Цитата(fray @  4.9.2007,  09:56 Найти цитируемый пост)
А зачем вообще через сигнал $SIG{CHLD} начинать ждать завершения процесса, может просто установить в родиделе wait() ?


Читай про "зомби"


--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
fray
Дата 5.9.2007, 12:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Цитата(Bulat @ 4.9.2007,  11:26)
fray, при форке как правило обмена нет, правда можно пользоватся пайпами или через запись в файл, но в таких случаях лучше уж чем-то другим, так проще. Кроме того зачастую совсем необязательно обратно что-то возвращять. При распарсивании сайтов(сбор ссылок и инфы), чем я сейчас занимаюсь, этого вполне достаточно, дочернему процессц вполне достаточно передать ссылку, так как конечные результаты будут в базе. Т.е. нам совсем не обязательно что-то еще возвращять. Дочерный процесс сам все сделает, занесет данные в базу... smile

Цитата(fray @  4.9.2007,  09:56 Найти цитируемый пост)
А зачем вообще через сигнал $SIG{CHLD} начинать ждать завершения процесса, может просто установить в родиделе wait() ?


Читай про "зомби"

А счего у тебя зомби образуются я просто делаю waitpid $_,0 for @pids; и никаких зомби, что там у тебя дедлок возникает ? 

PM MAIL   Вверх
Bulat
Дата 9.9.2007, 11:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



fray

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

А сипользовать обработку сигналов, лично для меня удобнее, ибо в целом можно установить некий уровень защиты, при обработке сигналов типа kill и т.п., мало ли сисадмин ошибется и убъет мой проц, когда еще тот не доработает smile Можно даже заставить процы спать, если это понадобится, так сказать тормознуть на время. Вообщем суть обработки сигналов для меня очень удобна smile


--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
G0rinich
Дата 2.10.2007, 10:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Для сетевой многозадачности используйте POE и не парьтесь ;) Почему, можно прочитать у меня здесь. Если будут вопросы, с удовольствием отвечу.

Если работа не связана с сетью, а представляет собой просто обработку данных, то при использовании подобной многозадачности вы скорее потеряете ;)

Добавлено через 32 секунды
подписку забыл включить smile
PM MAIL   Вверх
Bulat
Дата 3.10.2007, 09:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



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

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


--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
G0rinich
Дата 3.10.2007, 10:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



Bulat, ну так POE работает везде ;) Прикручиваешь модуль и все работает. Проверено. Плюс скорость разработки + отсутствие много гемора по организации многозадачности (за тебя это уже сделали) + еще много плюсов. Из пауков написанных на POE я выжимал 100-1000 запросов в секунду. Разгонялся до 20 Мбит/сек. Дальше пропускная способность DNS не давала :( И это все одним процессом ;)
PM MAIL   Вверх
Nab
Дата 3.10.2007, 14:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Bulat, слушай его, он дело говорит smile


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


татарский Нео
***


Профиль
Группа: Завсегдатай
Сообщений: 1701
Регистрация: 22.3.2006
Где: Альметьевск

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



G0rinich
Nab

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

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


--------------------
менеджер по кодеврайтингу  smile 
PM MAIL WWW   Вверх
G0rinich
Дата 6.10.2007, 17:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



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

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



POE - это не совсем ООП ;) Некоторые принципы конечно есть. А вот насчет кроссплатформенности там все очень хорошо, но лучше себя чувствует под никсами. Кстати, про виснущий запрос можно будет забыть, он никак не затормозит работы ;)
PM MAIL   Вверх
fray
Дата 8.10.2007, 22:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


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

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



Быстрые многопоточные проги к сожалению на перле не напишешь.

Это сообщение отредактировал(а) fray - 8.10.2007, 22:22
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Perl"
korob2001
sharq
  • В этом разделе обсуждаются общие вопросы по языку Perl
  • Если ваш вопрос относится к системному программированию, задавайте его здесь
  • Если ваш вопрос относится к CGI программированию, задавайте его здесь
  • Интерпретатор Perl можно скачать здесь ActiveState, O'REILLY, The source for Perl
  • Справочное руководство "Установка perl-модулей", можно скачать здесь


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

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


 




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


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

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