на главную | войти | регистрация | DMCA | контакты | справка | donate |      

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
А Б В Г Д Е Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Э Ю Я


моя полка | жанры | рекомендуем | рейтинг книг | рейтинг авторов | впечатления | новое | форум | сборники | читалки | авторам | добавить




Листинг 10.2. Псевдокод для задачи производителя и потребителя

Producer                                Consumer

инициализация семафора get значением 0;

инициализация семафора put значением 1;

for (;;) {                              for (;;) {

 sem_wait(&put);                         sem_wait(&get);

 помещение данных в буфер;               обработка данных в буфере;

 sem_post(&get);                         sem_post(&put);

}                                       }

Семафор put oгрaничивaeт возможность помещения объекта в общий буфер, а семафор get управляет потребителем при считывании объекта из буфера. Работает эта пpoгрaммa в такой последовательности:

1. Производитель инициализирует буфер и два семафора.

2. Пусть после этого запускается потребитель. Он блокируется при вызове sem_wait, поскольку семафор get имеет значение 0.

3. После этого запускается производитель. При вызове sem_wait значение put уменьшается с 1 до 0, после чего производитель помещает объект в буфер. Вызовом sem_post значение семафора get увеличивается с 0 до 1. Поскольку имеется поток, заблокированный в ожидании изменения значения этого семафора, этот поток помечается как готовый к выполнению. Предположим, тем не менее, что производитель продолжает выполняться. В этом случае он блокируется при вызове sem_wait в начале цикла for, поскольку значение семафора put — 0. Производитель должен подождать, пока потребитель не извлечет данные из буфера.

4. Потребитель возвращается из sem_wait, уменьшая значение семафора get с 0 до 1. Затем он обрабатывает данные в буфере и вызывает sem_post, увеличивая значение put с 0 до 1. Заблокированный в ожидании изменения значения этого семафора поток-производитель помечается как готовый к выполнению. Предположим опять, что выполнение потребителя продолжается. Тогда он блокируется при вызове sem_wait в начале цикла for, поскольку семафор get имеет значение 0.

5. Производитель возвращается из sem_wait, помещает данные в буфер, и все повторяется.

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

Перечислим три главных отличия семафоров и взаимных исключений в паре с условными переменными:

1. Взаимное исключение всегда должно разблокироваться тем потоком, который установил блокировку, тогда как увеличение значения семафора не обязательно осуществляется ожидающим его изменения потоком. Это мы только что продемонстрировали на примере.

2. Взаимное исключение может быть либо заблокировано, либо разблокировано (пара состояний, аналогично бинарному семафору).

3. Поскольку состояние семафора хранится в определенной переменной, изменение его значения оказывает влияние на процессы, которые вызовут функцию wait уже после этого изменения, тогда как при отправке сигнала по условной переменной в отсутствие ожидающих его потоков сигнал будет утерян. Взгляните на листинг 10.2 и представьте, что при первом проходе цикла производителем потребитель еще не вызвал sem_wait. Производитель сможет поместить объект в буфер, вызвать sem_post для семафора get (увеличивая его значение с 0 до 1), а затем он заблокируется в вызове sem_wait для семафора put. Через некоторое время потребитель дойдет до цикла for и вызовет sem_wait для переменной get, что уменьшит значение этого семафора с 1 до 0, а затем потребитель приступит к обработке содержимого буфера.

ПРИМЕЧАНИЕ

В Обосновании Posix.1 (Rationale) содержится следующий комментарий по поводу добавления семафоров помимо взаимных исключений и условных переменных: «Семафоры включены в стандарт в первую очередь с целью предоставить средства синхронизации выполнения процессов; эти процессы могут и не использовать общий сегмент памяти. Взаимные исключения и условные переменные описаны как средства синхронизации потоков, у которых всегда есть некоторое количество общей памяти. Оба метода широко используются уже много лет. Каждое из этих простейших средств имеет свой предпочтительный круг задач». В разделе 10.15 мы увидим, что для реализации семафоров-счетчиков с живучестью ядра требуется написать около 300 строк кода на С, использующего взаимные исключения и условные переменные. Несмотря на предпочтительность применения семафоров для синхронизации между процессами и взаимных исключений для синхронизации между потоками, и те и другие могут использоваться в обоих случаях. Следует пользоваться тем набором средств, который удобен в данном приложении.

Выше мы отмечали, что стандартом Posix описано два типа семафоров: именованные (named) и размещаемые в памяти (memory-based или unnamed). На рис. 10.4 сравниваются функции, используемые обоими типами семафоров.

Именованный семафор Posix был изображен на рис. 10.2. Неименованный, или размещаемый в памяти, семафор, используемый для синхронизации потоков одного процесса, изображен на рис. 10.5.

UNIX: взаимодействие процессов

Рис. 10.4. Вызовы для семафоров Posix


UNIX: взаимодействие процессов

Рис. 10.5. Семафор, размещенный в общей памяти двух потоков


На рис. 10.6 изображен размещенный в разделяемой памяти семафор (часть 4), используемый двумя процессами. Общий сегмент памяти принадлежит адресному пространству обоих процессов.

UNIX: взаимодействие процессов

Рис. 10.6. Семафор, размещенный в разделяемой двумя процессами памяти


В этой главе сначала рассматриваются именованные семафоры Posix, а затем — размещаемые в памяти. Мы возвращаемся к задаче производителей и потребителей из раздела 7.3 и расширяем ее, позволяя нескольким производителям работать с одним потребителем, а в конце концов переходим к нескольким производителям и нескольким потребителям. Затем мы покажем, что часто используемый при реализации ввода-вывода метод множественных буферов является частным случаем задачи производителей и потребителей.

Мы рассмотрим три реализации именованных семафоров Posix: с использованием каналов FIFO, отображаемых в память файлов и семафоров System V. 


Листинг 10.1. Сравнение бинарных семафоров и взаимных исключений | UNIX: взаимодействие процессов | 10.2. Функции sem_open, sem_close и sem_unlink