Этой статьёй я планирую завершить тему многопоточности в 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)
16 Январь 2008 в 18:36
Прикольные вещи. Сам последнее время балуюсь с этим.
Главные проблемы возникают когда надо ждать ответа от нити, чтобы запустить новую нить.
А так все идеально
16 Январь 2008 в 19:21
Про это я писал, если мне не изменяет память, во второй части цикла о многопоточности
Кстати, а Вы, как я посмотрю, софт такого рода пишите на PHP… Почему?
4 Март 2008 в 08:07
Что-то давно ты ничего не писал…
4 Март 2008 в 21:17
Здравствуй, Вася
Да, вынужден признать, что графомана из меня не получилось. На полезные темы меня писать ломает, а лытдыбрить скучно. Вот так и живём прошлым
А вот скажи-ка друг ситный, зачем тебе многопоточность, да ещё и перловая?
26 Март 2008 в 19:46
Здравствуйте! тут наperlmonks вычитал что есть такая плохая штука как копирование всего – цитата с http://www.perlmonks.org/index.pl?node_id=288022: It means that every time you start a thread all data structures are copied to the new thread. And when I say all, I mean all. This e.g. includes package stashes, global variables, lexicals in scope. Everything!
Что, по-прежнему так всё и обстоит?
28 Август 2008 в 06:28
# Создаём $threads нитей, каждая из которых будет
for (my $i = 0; $i create(\&find_proxy, $i);
}
Добрый день!Что-то здесь с ошибками?
С уважением, Леонид.
6 Февраль 2009 в 13:57
А как закрыть поток?
3 Март 2009 в 04:56
Да, действительно, парсер съел весь код. Там должно быть так:
for (my $i = 0; $i < $threads; $i++) {threads->new(\&find_proxy, $i);
}
Текст поста исправил, спасибо за внимательность.