KnigaRead.com/

Михаил Шохирев - Язык программирования Perl

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Михаил Шохирев, "Язык программирования Perl" бесплатно, без регистрации.
Перейти на страницу:

Существует несколько моделей многопоточной обработки, например DEC, Java, POSIX, Win32. Perl предлагает свою модель многопоточного программирования, отличающуюся от перечисленных и имеющую свои достоинства и недостатки. Появление в Perl кросс-платформенных средств работы с легковесными процессами стало несомненным достижением, которое заставило по-новому взглянуть на программирование параллельных процессов. Применение легковесных процессов позволяет разрабатывать эффективные приложения, одинаково выполняющиеся на разных платформах.

Работать с легковесными процессами просто. Подключив средства работы с нитями прагмой use threads, можно создать нить с помощью метода threads->new (синоним: threads->create). Этому методу передается ссылка на именованную или анонимную подпрограмму, которая запускается на выполнение в виде параллельного потока управления. Результатом создания нити станет ссылка на объект типа threads, который будет использоваться для управления потоком. Создание нити выглядит так:


use threads; # подключить многопоточные средства my $thread = threads->new(&pearl_thread); # запустить нить sub pearl_thread { # эта подпрограмма print "Это нить.n"; # будет выполняться как нить } #


Итак, в определенной точке программы нить начала выполняться параллельно действиям в основной программе. Куда же должен произойти возврат, когда нить завершится? Это задается в основной программе с помощью метода join, который приостанавливает работу программы до завершения выполнения нити и возвращает результат, вычисленный нитью:


@result = $thread->join;


Действие, выполняемое методом join, называется "присоединение нити" или "объединение потоков". Как это происходит, показано на рис. 16.2.

Рис. 16.2.  Присоединение нити с помощью join()

Каждой нити присваивается числовой идентификатор (Thread Identifier, TID), который можно получить с помощью метода tid. Создание нескольких нитей, объединение потоков и возврат значений из параллельно выполняющихся подпрограмм можно показать на таком примере:


use threads; # подключить многопоточные средства my @thread = (); # массив объектов типа threads for (my $i = 0; $i <= 2; $i++) { # создаем 3 нити $thread[$i] = threads->new(&pearl_thread, $i); print "Создана $i-я нить. TID=", $thread[$i]->tid, "n"; } for (my $i = 2; $i >= 0; $i--) { # присоединяем нити print "$i-я нить вернула ", $thread[$i]->join, "n"; } sub pearl_thread ($) { # нить получает my $number = shift; # число, генерирует my $random = int(rand(7)) + 1; # случайное значение, print "t$number-я нить ждет $random сек.n"; sleep $random; # и, подождав немного, return $random; # возвращает его }


Сообщения, выводимые при выполнении этой программы, подтверждают независимое выполнение нитей и основной программы:


Создана 0-я нить. TID=1 Создана 1-я нить. TID=2 1-я нить ждет 7 сек. 0-я нить ждет 1 сек. Создана 2-я нить. TID=3 2-я нить ждет 3 сек. 2-я нить вернула 3 1-я нить вернула 7 0-я нить вернула 1


Параллельно выполняющийся поток можно "отсоединить", игнорируя его значение: это делается методом $thread->detach, после выполнения которого присоединить нить будет невозможно.

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


use threads; # подключить многопоточные средства use threads::shared; # и средства разделения данных my $public : shared = 0; # разделяемая переменная my $private = 0; # неразделяемая переменная threads->new(sub { # нить из анонимной подпрограммы $public++; $private++; # изменяем значения print "$public $privaten"; # будет выведено: 1 1 })->join; # дожидаемся результатов выполнения: print "$public ", # 1 ($public изменена в нити) "$privaten"; # 0 (в нити изменена копия $private)


Чтобы предотвратить в одной нити изменение другими нитями значения разделяемой переменной, эту переменную нужно заблокировать при помощи функции lock(). Разблокирование переменной происходит не с помощью функции, а при выходе из блока, в котором она была заблокирована. Это делается таким образом:


{ # блок для работы с разделяемой переменной lock $variable; # заблокировать переменную $variable = $new_value; # и изменить ее значение } # здесь $variable автоматически разблокируется


Нити могут обмениваться между собой данными. Например, с помощью стандартного модуля Thread::Queue организуется очередь для синхронизированной передачи данных из одной нити в другую. Пользоваться такой очередью гораздо проще, чем рассмотренными ранее программными каналами. Небольшой пример показывает, как помещать скалярные величины в очередь методом enqueue() и извлекать из нее методом dequeue(). Метод pending() возвращает число оставшихся в очереди элементов, поэтому может использоваться для окончания цикла чтения из очереди:


use threads; # подключить средства use Thread::Queue; # и модуль работы с очередью my $data_queue = Thread::Queue->new; # создаем очередь my $thread = threads->new(&reader); # и нить # помещаем в очередь скалярные данные: $data_queue->enqueue(1987); # число $data_queue->enqueue('год'); # строку $data_queue->enqueue('рождения', 'Perl'); # список $thread->join; # ожидаем окончания нити exit; # перед завершением программы sub reader { # извлекаем данные из очереди, while ($data_queue->pending) { # пока она не пуста my $data_element = $data_queue->dequeue; print "'$data_element' извлечен из очередиn"; } }


Автоматическая синхронизация доступа к очереди гарантирует очередность записи в очередь и чтение из нее, что видно из выполнения этого примера:


'1987' извлечен из очереди 'год' извлечен из очереди 'рождения' извлечен из очереди 'Perl' извлечен из очереди


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

Существует много ситуаций, когда применение многозадачности не только оправданно, но и является единственно правильным решением задачи. Поэтому знание средств управления процессами дает вам новую точку зрения на решаемую проблему и расширяет ваш арсенал программных инструментов. В 6-й версии языка Perl средства распределенного программирования будут улучшены и дополнены, появятся сопрограммы (co-routines) и другие интересные возможности.

Лекция 17. Работа в IP-сетях

В этой лекции рассказывается об имеющихся в Perl возможностях обработки данных в IP-сетях, начиная с низкоуровневых средств и заканчивая классами для работы с основными сетевыми протоколами.

Цель лекции: узнать основные механизмы передачи данных по сети и научиться применять их для обработки данных в своих программах, используя стандартные и дополнительные модули Perl.

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

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

Для идентификации объединенных в сети компьютеров или других сетевых устройств, обобщенно называемых хостами (host), используются последовательности из четырех чисел - IP-адреса: например, 192.168.82.83 или 172.16.2.73. Назначенный IP-адрес служит уникальным идентификатором хоста в конкретной сети. Кроме того, каждый хост, настроенный на работу с протоколом IP и даже не подключенный к сети, имеет собственный специальный адрес 127.0.0.1 - что-то вроде местоимения "я" на языке сетевых коммуникаций. Хост может иметь доменное имя, соответствующее его IP-адресу, например, имя хоста www.perl.com соответствует адресу 208.201.239.36. Собственному адресу 127.0.0.1 соответствует специальное имя localhost. Поскольку на каждом хосте может выполняться несколько сетевых программ, то для распределения между ними получаемых и отправляемых пакетов используются дополнительные числовые обозначения, так называемые номера портов. Поэтому программная точка отправления или доставки данных в IP-сетях определяется сочетанием адреса и порта, разделенных двоеточием. Многие номера портов по общепринятым соглашениям закреплены за определенными сетевыми службами. Например, обращение к web-серверу на текущей машине будет происходить по адресу и порту 127.0.0.1:80, а к почтовому серверу - по 127.0.0.1:25.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*