print "\tНачался потомок PID $$ предка $parent\n";
for (1..7) {
print "\tПотомок PID $$ работает $_\n";
sleep 1; # 'заснуть' на 1 секунду
}
print "\tЗакончился потомок PID $$\n";
}
Поведение этих процессов во время выполнения программы можно проследить по выводимым ими сообщениям:
Начался потомок PID -800 предка 696
Потомок PID -800 работает 1
Начался предок PID 696
Предок PID 696 работает 1
Предок PID 696 отправил сигнал
Потомок получил 1-й сигнал!
Потомок PID -800 работает 2
Предок PID 696 работает 2
Предок PID 696 отправил сигнал
Потомок PID -800 работает 3
Потомок получил 2-й сигнал!
Потомок PID -800 работает 4
Предок PID 696 работает 3
Предок PID 696 отправил сигнал
Потомок PID -800 работает 5
Потомок получил 3-й сигнал!
Потомок PID -800 работает 6
Закончился предок (PID 696)
Потомок PID -800 работает 7
Закончился потомок PID -800
Сигналы нельзя считать слишком надежным и информативным средством обмена информацией: для передачи данных лучше использовать другие способы. Зато можно проверить состояние дочернего процесса, отправив ему особый нулевой сигнал функцией kill(0, $pid). Этот вызов не влияет на выполнение процесса-потомка, но возвращает истину (1), если процесс "жив", и ложь (0), если он завершился или ему нельзя посылать сигналы. Одинаковая реакция на нулевой сигнал гарантируется на различных платформах. Кроме того, можно прекратить выполнение дочернего процесса, отправив ему сигнал KILL вызовом kill(KILL, $pid).
В последних версиях Perl появилась еще одна модель многозадачности - легковесные процессы (light-weight processes), называемые также потоками управления или нитями. (По-английски фраза "Perl threads" звучит как каламбур и может быть переведена как "нитки жемчуга" или "жемчужные ожерелья"). Нити отличаются от полновесных процессов с независимыми ресурсами тем, что выполняются в рамках одного процесса в единой области памяти. Поэтому создание нити происходит быстрее запуска отдельного процесса и требует меньше ресурсов операционной системы. Выполнение нитей в одной области памяти позволяет эффективно организовать совместный доступ к разделяемым данным. Кроме того, программист получает более полный контроль над параллельно выполняющимися потоками управления. Принципиальное различие между полновесными процессами, созданными операционной системой, и многопоточными нитями показано на рис. 16.1.
Рис. 16.1.Полновесные процессы и нити (потоки управления)
Существует несколько моделей многопоточной обработки, например 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. Создание нескольких нитей, объединение потоков и возврат значений из параллельно выполняющихся подпрограмм можно показать на таком примере: