Современные
Во время выполнения процессы могут взаимодействовать между собой различными способами. Они могут иметь доступ к разделяемой области памяти, организовывать программные каналы (
Реализация этих
В qx(), упоминавшейся в лекции 7. Она предназначена для получения dir, выводящую
my $file_list = `dir`; # в скалярном контексте my @file_list = qx(dir); # в списочном контексте
В зависимости от того, в каком
Выполнить внешнюю программу можно также с помощью функции , которая организует синхронный запуск программы и возвращает
use English; # использовать длинные имена спец. переменных
# в ОС MS Windows архивируем файлы с помощью pkzip
if ($OSNAME =~ m/win/i) {
system "pkzip", "-a", "pearls.zip", "*.pl";
# в ОС GNU/Linux архивируем файлы с помощью tar и gzip
} elsif ($OSNAME =~ m/linux/i) {
system "tar -cv *.pl | gzip > pearls.tar.gz";
}
При вызове с одним использует для запуска , и операция qx(). При передаче ей нескольких аргументов она запускает внешнюю программу с помощью системного вызова (обращения к
{ # временно помещаем каталог с программой в пути поиска
local $ENV{"PATH"} = $path_to_the_program; # каталог
system($program_to_execute); # вызов программы
} # значение $ENV{"PATH"} будет восстановлено
Выполнение внешних программ можно организовать с помощью функции , если требуется обмениваться данными с этими программами, используя вместо имени файла передается командная строка с именем выполняемой программы и ее аргументами. Если нужно передать '|'. Как это делается, видно из очень простого примера, в котором случайным образом генерируются числовые gzip:
# открываем выходной поток, направляем его внешней программе
open my $archive, "| gzip > passwords.gz";
for (my $i = 1; $i <= 12; $i++) { # генерируем пароли
printf $archive "%06.0f\n", rand 999999;
}
close $archive; # закрываем выходной поток
Когда нужно принять выходной поток внешней программы для обработки в '|' ставится в конце
# открываем входной поток, полученный от внешней программы
open my $archive, "gzip -d < passwords.gz |";
while (my $line = <$archive>) { # читаем пароли из архива
print $line;
}
close $archive; # закрываем выходной поток
(Используемый в примерах
Иногда требуется организовать выполнение программы таким образом: вначале запускается
print "Выполняется загрузчик: $0, PID:$$\n"; # заменить текущую программу на указанную my $program = $ARGV[0]; # имя программы в 1-м аргументе print "Запускается программа: $program\n"; exec 'perl', $program or die; # запуск программы print "Это сообщение никогда не напечатается!\n";
При запуске этого примера с 'proc_executed. будут выведены такие сообщения:
Выполняется загрузчик: proc_exec.pl, PID:652 Запускается программа: proc_executed.pl Выполняется программа: proc_executed.pl, PID:1872
Для организации параллельного выполнения процессов в . В других организуется исполняющей системой в
my $pid = fork(); # 'разветвить' текущий процесс
# fork вернет 0 в потомке и PID потомка в процессе-предке
die "fork не отработал: $!" unless defined $pid;
unless ($pid) { # процесс-потомок
print "Начался потомок PID $$\n";
for (1..3) {
print "Потомок PID $$ работает $_\n";
sleep 2; # 'заснуть' на 2 секунды
}
print "Закончился потомок PID $$\n";
exit;
}
if ($pid) { # процесс-предок
print "Начался предок PID $$\n";
for (1..3) {
print "Предок PID $$ работает $_\n";
sleep 1; # 'заснуть' на 1 секунду
}
# возможно, здесь нужно ждать завершения потомка:
# print "Предок PID $$ ждет завершения $pid\n";
# waitpid $pid, 0;
print "Закончился предок PID $$\n";
}
По сообщениям, выводимым при выполнении этого примера, видно, что родительский и порожденный процессы выполняются параллельно. Для того чтобы организовать в waitpid(), которой передается waitpid ):
Без ожидания потомка С ожиданием потомка по waitpid()
---------------------------- --------------------------------
Начался потомок PID -1024 Начался потомок PID -1908
Потомок PID -1024 работает 1 Потомок PID -1908 работает 1
Начался предок PID 1504 Начался предок PID 1876
Предок PID 1504 работает 1 Предок PID 1876 работает 1
Предок PID 1504 работает 2 Предок PID 1876 работает 2
Потомок PID -1024 работает 2 Потомок PID -1908 работает 2
Предок PID 1504 работает 3 Предок PID 1876 работает 3
Закончился предок PID 1504 Предок PID 1876 ждет завершения -1908
Потомок PID -1024 работает 3 Потомок PID -1908 работает 3
Закончился потомок PID -1024 Закончился потомок PID -1908
Закончился предок PID 1876
Выполнение всей программы заканчивается, когда заканчивается последний , которая возвращает
В , которой передаются имена двух файловых манипуляторов: приемника и источника. Один из вариантов взаимодействия процессов через программный
use IO::Handle; # подключаем стандартный модуль
pipe(READER, WRITER); # создаем программный канал
WRITER->autoflush(1); # включаем авто-очистку буфера
if ($pid = fork()) { # процесс-предок получает PID потомка
close READER; # предок не будет читать из канала
print WRITER "Послано предком (PID $$):\n";
for (my $n = 1; $n <= 5; $n++) { # запись в передатчик
print WRITER "$n ";
}
close WRITER; # закрываем канал и
waitpid $pid, 0; # ждем завершения потомка
}
die "fork не отработал: $!" unless defined $pid;
if (!$pid) { # процесс-потомок получает 0
close WRITER; # предок не будет писать в канал
print "Потомок (PID $$) прочитал:\n";
while (my $line = <READER>) { # чтение из приемника
print "$line";
}
close READER; # канал закрывается
exit; # потомок завершает работу
}
Во время выполнения этого примера в стандартный выходной поток будет выведено следующее:
Потомок (PID -2032) прочитал: Послано предком (PID 372): 1 2 3 4 5
Если нужно организовать
Кроме программных каналов, процессы могут обмениваться информацией и другими способами: через именованные каналы (
В %. Поэтому допустимые keys(%. Общеизвестный пример -
# устанавливаем обработчик сигнала INT
$SIG{INT} = \sig_handler; # ссылка на подпрограмму
# начало основной программы
print "Работаю в поте лица...\n" while (1); # бесконечный цикл
sub sig_handler { # подпрограмма-обработчик сигнала
$SIG{INT} = \sig_handler; # переустанавливаем обработчик
print "Получен сигнал INT по нажатию Ctrl+C\n";
print "Заканчиваю работу!\n";
exit; # завершение выполнения программы
}
Выполнение примера сопровождается выводом сообщений, подтверждающих обработку поступившего сигнала:
Работаю в поте лица... Получен сигнал INT по нажатию Ctrl+C Заканчиваю работу!
Примером реальной программы, выполняющейся в бесконечном цикле, может служить любой '. Восстановить стандартную обработку сигнала можно, присвоив соответствующему элементу % строку '.
Процесс может посылать сигналы самому себе, например, для отслеживания окончания запланированного периода времени (для обработки тайм-аута). В приведенном примере
# устанавливаем обработчик сигнала ALRM (будильник)
$SIG{ALRM} = sub { die "Timeout"; }; # анонимная подпрограмма
$timeout = 3600; # определяем величину тайм-аута (сек.)
eval { # блок обработки возможной ошибки
alarm($timeout); # устанавливаем время отправки сигнала
# некая длительная операция:
print "Работаю в поте лица...\n" while (1);
alarm(0); # нормальное завершение операции
};
# в специальной переменной $@ - сообщение об ошибке
if ($@ =~ /Timeout/) { # проверяем причину ошибки
print "Аварийный выход по истечении времени!";
}
Отправка сигнала одним процессом другому также используется в качестве средства взаимодействия процессов. Сигнал отправляется процессу с помощью функции , которой передаются два
my $parent = $$; # PID родительского процесса
my $pid = fork(); # 'разветвить' текущий процесс
# fork вернет PID потомка в процессе-предке и 0 в потомке
die "fork не отработал: $!" unless defined $pid;
if ($pid) { # ---------- родительский процесс ----------
print "Начался предок PID $$\n";
for (1..3) {
print "Предок PID $$ работает $_\n";
print "Предок PID $$ отправил сигнал\n";
kill HUP, $pid;
sleep 2; # 'заснуть' на 2 секунды
}
print "Закончился предок (PID $$)\n";
}
unless ($pid) { # ---------- дочерний процесс ----------
my $counter = 0; # счетчик полученных сигналов
$SIG{HUP} = sub { ### обработчик сигнала ###
$counter++;
print "\tПотомок получил $counter-й сигнал!\n";
}; ### конец обработчика сигнала ###
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
Сигналы нельзя считать слишком надежным и информативным средством обмена информацией: для . Этот вызов не влияет на выполнение процесса-потомка, но возвращает .
В последних версиях
(рис 16.1) Полновесные процессы и нити (потоки управления)Существует несколько моделей многопоточной обработки, например
Работать с легковесными процессами просто. ( ). Этому методу передается
use threads; # подключить многопоточные средства
my $thread = threads->new(\pearl_thread); # запустить нить
sub pearl_thread { # эта подпрограмма
print "Это нить.\n"; # будет выполняться как нить
} #
Итак, в определенной точке программы нить начала выполняться параллельно действиям в , который приостанавливает работу программы до завершения выполнения нити и возвращает
@result = $thread->join;
Действие, выполняемое методом
(рис 16.2) Присоединение нити с помощью join()Каждой нити присваивается числовой . Создание нескольких нитей,
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
Параллельно выполняющийся поток можно "отсоединить", игнорируя его значение: это делается методом $, после выполнения которого присоединить нить будет невозможно.
Нити, выполняющиеся параллельно с основной программой, могут иметь доступ к общим переменным: скалярам, массивам и хэшам. Это делается с помощью явного указания для разделяемой . Помеченная этим атрибутом переменная будет доступна для чтения и изменения в параллельном потоке. Для остальных
use threads; # подключить многопоточные средства
use threads::shared; # и средства разделения данных
my $public : shared = 0; # разделяемая переменная
my $private = 0; # неразделяемая переменная
threads->new(sub { # нить из анонимной подпрограммы
$public++; $private++; # изменяем значения
print "$public $private\n"; # будет выведено: 1 1
})->join; # дожидаемся результатов выполнения:
print "$public ", # 1 ($public изменена в нити)
"$private\n"; # 0 (в нити изменена копия $private)
Чтобы предотвратить в одной нити изменение другими нитями значения разделяемой . Разблокирование
{ # блок для работы с разделяемой переменной
lock $variable; # заблокировать переменную
$variable = $new_value; # и изменить ее значение
} # здесь $variable автоматически разблокируется
Нити могут обмениваться между собой данными. Например, с помощью организуется очередь для синхронизированной и извлекать из нее методом . Метод возвращает число оставшихся в очереди элементов, поэтому может использоваться для окончания цикла чтения из очереди:
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' извлечен из очереди
Конечно, имеющиеся в
Существует много ситуаций, когда применение
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.