Ваше требование к случайности очень странно, но если я правильно понял ваши требования, вам не понадобится никакой блокировки, чтобы делать то, что вы хотите.(Так что 1)
в вашем вопросе пропал)
Начните с перетасовки массива программы, затем запустите каждую команду этого перетасованного массива (это касается вашего 4)
).Тогда только waitpid
после вы запустили все (что касается ваших 2)
).
Код, приведенный ниже, делает это, начиная различные экземпляры sleep
в новых терминалах (Iиспользуйте urxvt
, адаптируйтесь в зависимости от того, какой терминал вы хотите породить - это касается вашего 3)
).
#! /usr/bin/perl -w
use strict;
use warnings;
my @progs = ("urxvt -e sleep 5", "urxvt -e sleep 2", "urxvt -e sleep 1");
my @sgrop;
my @pids;
# Shuffle the programs
while (my $cnt = scalar(@progs)) {
push @sgrop, splice @progs, int(rand($cnt)), 1;
}
# Start the progs
foreach my $prog (@sgrop) {
my $pid = fork();
if (!$pid) {
exec($prog);
# exec does not return
} else {
print "Started '$prog' with pid $pid\n";
push @pids, $pid;
}
}
# Wait for them
map {
waitpid($_, 0);
print "$_ done!\n";
} (@pids);
Не уверен, что тасование является лучшим, но оно работает.Идея заключается в том, чтобы просто выбрать один элемент случайным образом из исходного (отсортированного) списка, удалить его оттуда и добавить в перемешанный.Повторяйте до тех пор, пока начальный список не станет пустым.
Если вы пытаетесь заблокировать программы в масштабе всей системы (т. Е. Ни один другой процесс в вашей системе не может их запустить), то извините, но это невозможноесли только программы не защищают себя от одновременного выполнения.
Если ваш вопрос касался семафоров, то извините, я упустил вашу мысль.Документация IPC содержит пример кода для этого.Я действительно не думаю, что нужно идти к такой сложности для того, что вы пытаетесь сделать.
Вот как вы могли бы сделать это, используя модуль IPC::Semaphore
для удобства.
В начале вашего основного файла создайте набор семафоров с таким количеством семафоров, сколько потребуется:
use IPC::SysV qw(S_IRUSR S_IWUSR IPC_CREAT IPC_NOWAIT);
use IPC::Semaphore;
my $numprocs = scalar(@progs);
my $sem = IPC::Semaphore->new(1234, # this random number is the semaphore key. Use something else
$numprocs, # number of semaphores you want under that key
S_IRUSR | S_IWUSR | IPC_CREAT);
Проверьте ошибки, затем инициализируйте все семафоры до 1.
$sem->setall( (1) x $numprocs) || die "can't set sems $!";
В коде, который запускает ваши процессы, прежде чем запускать (хотя после разветвления), попробуйте захватить семафор:
if ($sem->op($proc_number, -1, IPC_NOWAIT)) {
# here, you got the semaphore - so nothing else is running this program
# run the code
# and once the code is done:
$sem->op($proc_number, 1, 0); # release the semaphore
exit(0);
} else {
# someone else is running this program already
exit(1); # or something
}
В приведенном выше примере $proc_number
должно быть уникальным для каждогопрограмма (это может быть, например, индекс в массиве программ). Не используйте exec
для запуска программы.Вместо этого используйте, например, system
.
Обратите внимание, что в этом случае придется обработать код выхода дочернего процесса.Если код выхода равен нулю, вы можете пометить эту программу как запущенную.Если нет, вам нужно повторить попытку.(Это может запутаться, вам нужно будет отслеживать, какая программа была запущена или нет. Я бы предложил хеш с номером программы ($proc_number
), где вы бы сохранили, была ли она уже завершена или нет, итекущий pid запускает (или пытается запустить) этот код. Вы можете использовать этот хеш, чтобы выяснить, какую программу еще нужно выполнить.)
Наконец, после того, как все сделано, и вы дождались всех потомков,Вы должны очистить после себя:
$sem->remove;
В этом коде отсутствует правильная проверка ошибок, он будет работать странно (то есть не очень хорошо), если очистка не была выполнена правильно (т.е. семафоры уже лежат, когда кодначинается).Но это должно начать вас.