з

К предыдущей странице

л

К предыдущей главе

о

К следующей главе

и

К следующей странице


4.4. Семафоры как счетчики ресурсов и синхронизаторы операций

Рассмотрим использование семафоров на классическом примере взаимодействия двух процессов, выполняющихся в режиме мультипрограммирования, один из которых пишет данные в буферный пул, а другой считывает их из буферного пула. Пусть буферный пул состоит из N буферов, каждый из которых может содержать одну запись. Процесс "писатель" должен приостанавливаться, когда все буфера оказываются занятыми, и активизироваться при освобождении хотя бы одного буфера. Напротив, процесс "читатель" приостанавливается, когда все буферы пусты, и активизируется при появлении хотя бы одной записи. Введем два семафора: e - число пустых буферов и f - число заполненных буферов. Предположим, что запись в буфер и считывание из буфера являются критическими секциями (как в примере с принт-сервером в начале данного раздела). Введем также двоичный семафор b, используемый для обеспечения взаимного исключения. Тогда процессы могут быть описаны следующим образом:

// Глобальные переменные #define N 256 int e = N, f = 0, b = 1; void Writer (){ while(1){ PrepareNextRecord(); /* подготовка новой записи */ P(e); /* Уменьшить число свободных буферов, если они есть */ /* в противном случае - ждать, пока они освободятся */ P(b); /* Вход в критическую секцию */ AddToBuffer(); /* Добавить новую запись в буфер */ V(b); /* Выход из критической секции */ V(f); /* Увеличить число занятых буферов*/ } } void Reader () { while(1) { P(f); /* Уменьшить число занятых буферов, если они есть */ /* в противном случае ждать, пока они появятся */ P(b); /* Вход в критическую секцию */ GetFromBuffer(); /* Взять запись из буфера */ V(b); /* Выход из критической секции */ V(e); /* Увеличить число свободных буферов */ ProcessRecord(); /* Обработать запись */ } }

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

процессу предоставляется возможность ждать, пока другой не сообщит о "свершении" определенного события.

В качестве примера возьмем пару процессов:

Pr1 - производитель;

Pr2 - потребитель.

Пусть Pr1 создает записи по одной за кt, а Pr2 использует их по одной за кt.

Произведенная, но еще не затребованная запись помещается в буфер размером в одну запись. Процессы должны быть синхронизированы так, чтобы между любыми двумя последовательными записями в буфер, выполнялось в точности одно чтение и наоборот.

Pr1 -пишет i-ую (первую запись) в буфер и совершает событие - НАЧАЛО - это событие позволяет потребителю начать обработку записи.

Pr2 - читает i-ую запись из буфера и совершает событие - КОНЕЦ - это позволяет производителю (Pr1) записать в буфер i+1 запись.

Специально предусматривать взаимное исключение при доступе к буферу нет необходимости, т.к. в данном случае оно обеспечивается синхронизацией. Итак - семафор может быть синхронизатором, координирующим производство и потребление ресурсов. Процесс, потребляя ресурс, выполняет P-операцию над связанным с ресурсом семафором (т.е. Р(S)), что означает изменение значения S в меньшую сторону. Процесс производит ресурс, выполняя V(S)- операцию над тем же семафором.


з

К предыдущей странице

л

К предыдущей главе

о

К следующей главе

и

К следующей странице