6 Ноябрь 2007...23:52

Опять многопоточность, но теперь в PERL (часть 3-я и последняя)

Перейти к комментариям

Этой статьёй я планирую завершить тему многопоточности в Perl, ибо она меня уже порядком устала :) . Кстати, предыдущие статьи ни в коем случае не претендуют на полный и подробный мануал, вовсе нет. Это скорее HOWTO, дающее, однако возможность подумать и понять сущность процесса (как мне кажется).

Итак, на повестке дня —
Частенько смысл множества потоков состоит в работе над одной большой (или необязательно большой) базой. Возникает резонный вопрос: а как обмениваться информацией между нитями?

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

Как всегда пример:

#!/usr/bin/perl -w

use threads;
use threads::shared;

# Объявляем переменную общей для всех нитей с
# помощью модификатора shared
my $i : shared = 1;

# Альтернативный способ - использование функции share
my $j = 0;
share $j;

threads->new(\&my_sub)->join();
threads->new(\&my_sub)->join();
threads->new(\&my_sub)->join();

sub my_sub()
{
    print "Это вызов #$i. Переменная \$j = $j\n";
    $j++;
    $i++;
}

Результат такого скрипта в консоли выглядит примерно так:

% perl threads.pl
Это вызов #1. Переменная $j = 0
Это вызов #2. Переменная $j = 1
Это вызов #3. Переменная $j = 2

Вроде бы всё понятно и просто и пора уже с многопоточностью заканчивать. В качестве прощания мы с вами напишем скрипт, который будет выдирать прокси с первых пяти страниц небезызвестного ресурса MPR Proxylist (разумеется, каждая страница отдельным потоком :) ). В качестве HTTP-клиента используется библиотека LWP, которая обычно установлена (если не установлена, то пользователи FreeBSD могут устанавливать порт www/p5-libwww, а линуксоидам ничем помочь не могу :) ):


#!/usr/bin/perl -w

use LWP::UserAgent;
use threads;
use threads::shared;

# В этом массиве будут храниться найденные прокси
my @proxies : shared;

# Массив для хранения ссылок на объекты нитей
my @threads;

# Количество страниц (и, разумеется,  нитей :) )
my $threads = 5;

# Создаём $threads нитей, каждая из которых будет
for (my $i = 0; $i < $threads; $i++) {
    threads->new(\&find_proxy, $i);
}

# Скрипт будет жить до завершения работы всех нитей
foreach my $thread (@threads) {
    $thread->join();
}

# Записываем найденные прокси в файл
open PROXIES, '>', 'proxylist.txt';

foreach my $proxy (@proxies) {
    print PROXIES $proxy, "\n";
}

close PROXIES;

# Создание HTTP-клиента на основе LWP (при кажом создании
# у клиента будет разные User-Agent
sub http_factory()
{
    my @agents = ('Mozilla 5.0/Firefox',
                  'Opera 9.02',
                  'Interner Explorer 7',
                  'Safari 3.0/Gecko 31337',
                  'Lynx/FreeBSD 6.0');
    my $client = LWP::UserAgent->new('agent' => $agents[rand($#agents)]);
    return $client;
}

# Процедура соединения с донором и парсинг прокси
sub find_proxy
{
    my $page = shift;
    my $url = sprintf('http://proxylist.sakura.ne.jp/index.htm?pages=%s', $page);
    my $client = &http_factory();
    my $response = $client->get($url) or warn "Can't connect $url: $@\n";

    if ($response->is_success() && $response->content_type() eq 'text/html') {
        my $content = $response->content();

        while ($content =~ m!
(.*?)

\s*?
([a-z]{2})

!sgi) {
            my $proxy = $1;
            my $country = $2;
            push @proxies, $proxy;
            print "Found $proxy [$country], saving it\n";
        }
    }

    else {
        print "fuck! $@\n";
    }
}

В общем, вроде всё, балуйтесь, детишки. И не забывайте, что это только пример, демонстрирующий мощность многопоточных приложений на Perl :)

Удачного дня :)

Комментарии (8)


Ответить

You must be logged in to post a comment.

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