![]() |
![]() |
Главная страница Взаимодействие нетривиальных процессов solans % semwait /test2 pid 4133 has semaphore, value = 0 нажимаем клавишу прерывания выполнения Solaris % semgetvalue /test2 value = 0 Посмотрим теперь, как меняется значение семафора в этой реализации при появлении новых процессов, ожидающих изменения значения семафора: Solaris % semgetvalue /test2 value = О значение сохранилось с конца предыдущего примера Solaris % semwait /test2 & запуск в фоновом режиме [1]4257 программа блокируется Solaris % semgetvalue /test2 value = 0 в зтой реализации отрицательные значения не используются Solaris % serrwait /test2 & еще один фоновый процесс [2]4263 Solaris % semgetvalue /test2 value - О и для двух ожидающих процессов значение остается нулевым Solaris % sempost /test2 выполняем операцию post pid 4257 has semaphore, value = 0 вывод программы semait value = 0 Solaris % sempost /test2 pid 4263 has semaphore, value = 0 вывод программы semait value = 0 Можно заметить отличие по сравнению с результатами выполнения той же последовательности команд в Digital Unix 4.0В: после изменения значения семафора управление сразу же передается ожидающему изменения семафора процессу. 10.6. Задача производителей и потребителей в разделе 7.3 мы описали суть задачи производителей и потребителей и привели несколько возможных ее рещений, в которых несколько потоков-производителей заполняли массив, который обрабатывался одним потоком-потребителем. 1. В нащем первом варианте решения (раздел 7.2) потребитель запускался только после заверщения работы производителей, поэтому мы могли решить проблему синхронизации, используя единственное взаимное исключение для синхронизации производителей. 2. В следующем варианте решения (раздел 7.5) потребитель запускался до завершения работы производителей, поэтому требовалось использование взаимного исключения (для синхронизации производителей) вместе с условной переменной и еще одним взаимным исключением (для синхронизации потребителя с производителями). Расширим постановку задачи производителей и потребителей, используя общий буфер в качестве циклического: заполнив последнее поле, производитель (buff [NBUFF-1]) возвращается к его началу и заполняет первое поле (buff [0]), и потребитель действует таким же образом. Возникает еще одно требование к синхронизации: потребитель не должен опережать производителя. Мы все еще пред- полагаем, что производитель и потребитель представляют собой отдельные потоки одного процесса, но они также могут быть и просто отдельными процессами, если мы сможем создать для них общий буфер (например, используя разделяемую память, часть 4). При использовании общего буфера в качестве циклического код должен удовлетворять трем требованиям: 1. Потребитель не должен пытаться извлечь объект из буфера, если буфер пуст. 2. Производитель не должен пытаться поместить объект в буфер, если последний полон. 3. Состояние буфера может описываться общими переменными (индексами, счетчиками, указателями связных списков и т. д.), поэтому все операции с буфером, соверщаемые потребителями и производителями, должны быть защищены от потенциально возможной ситуации гонок. Наще рещение использует три семафора: 1. Бинарный семафор с именем mutex защищает критические области кода: помещение данных в буфер (для производителя) и изъятие данных из буфера (для потребителя). Бинарный семафор, используемый в качестве взаимного исключения, инициализируется единицей. (Конечно, мы могли бы воспользоваться и обычным взаимным исключением вместо двоичного семафора, См. упражнение 10.10.) 2. Семафор-счетчик с именем nempty подсчитывает количество свободных полей в буфере. Он инициализируется значением, равным объему буфера (NBUFF). 3. Семафор-счетчик с именем nstored подсчитывает количество заполненных полей в буфере. Он инициализируется нулем, поскольку изначально буфер пуст. На рис. 10.7 показано состояние буфера и двух семафоров-счетчиков после заверщения инициализации. Неиспользуемые элементы массива выделены темным. buff [0] buff [1] buff [2] buff [3] buff [NBUFF-1]: nempty: nstored: NBUFF Рис. 10.7. Состояние буфера и двух семафоров-счетчиков после инициализации В нашем примере производитель помещает в буфер целые числа от О до NLOOP -1 (buff [0] = О, buff [1] = 1), работая с ним как с циклическим. Потребитель считывает эти числа и проверяет их правильность, выводя сообщения об ошибках в стандартный поток вывода. На рис. 10.8 изображено состояние буфера и семафоров-счетчиков после помещения в буфер трех элементов, но до изъятия их потребителем. производитель помещает в буфер три элемента -> bufT[0] -> bufT[1]; -> buff [2] buff[3]; buff [NBUFF-1]: nempty: nstored: NBUFF-3 Рис. 10.8. Буфер и семафоры после помещения в буфер трех элементов Предположим, что потребитель изъял один элемент из буфера. Новое состояние изображено на рис. 10.9. buff [NBUFF-1]:
потребитель изымает 1 элемент из буфера nempty: nstored: NBUFF-2 Рис. 10.9. Буфер и семафоры после удаления первого элемента из буфера В листинге 10.8 приведен текст функции main, которая создает три семафора, запускает два потока, ожидает их завершения и удаляет семафоры. Листинг 10.8. Функция main для решения задачи производителей и потребителей с помощью семафоров pxsem/prodconsl.c 1 linclude unpipc.h 2 Idefine NBUFF 10
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |