Главная Случайная страница Контакты | Мы поможем в написании вашей работы! | ||
|
В качестве примера использования этих примитивов рассмотрим проблему производителя и потребителя, также известную как проблема ограниченного буфера. Два процесса совместно используют буфер ограниченного размера. Один из них, производитель, помещает данные в этот буфер, а другой, потребитель, считывает их оттуда. (Можно обобщить задачу на случай т производителей и п потребителей, но мы рассмотрим случай с одним производителем и одним потребителем, поскольку это существенно упрощает решение.)
Трудности начинаются в тот момент, когда производитель хочет поместить в буфер очередную порцию данных и обнаруживает, что буфер полон. Для производителя решением является ожидание, пока потребитель полностью или частично не очистит буфер. Аналогично, если потребитель хочет забрать данные из буфера, а буфер пуст, потребитель уходит в состояние ожидания и выходит из него, как только производитель положит что-нибудь в буфер и разбудит его.
Это решение кажется достаточно простым, но оно приводит к состояниям состязания, как и пример с каталогом спулера. Нам нужна переменная count для отслеживания количества элементов в буфере. Если максимальное число элементов, хранящихся в буфере, равно N, программа производителя должна проверить, не равно ли ^значение count прежде, чем поместить в буфер следующую порцию данных. Если значение count равно N, то производитель уходит в состояние ожидания; в противном случае производитель помещает данные в буфер и увеличивает значение count.
Код программы потребителя прост: сначала проверить, не равно ли значение count нулю. Если равно, то уйти в состояние ожидания; иначе забрать порцию данных из буфера и уменьшить значение count. Каждый из процессов также должен проверять, не следует ли активизировать другой процесс, и в случае необходимости проделывать это. Листинг Программы обоих процессов выглядят так:
#define N 100 /* Максимальное количество элементов в буфере */
int. count – 0; /* Текущее количество элементов в буфере */
void producer(void)
{
Int item:
while (TRUE) { /* Повторять вечно */
item = produce_item(); /* Сформировать следующий элемент */
if (count == N) slee(); /* Если буфер полон, уйти в состояние ожидания */
insert_item(item); /* Поместить элемент в буфер */
count = count +1; /* Увеличить количество элементов в буфере */
if (count == 1) wakeup(consumer); /* Был ли буфер пуст? */
}
}
void consumer(void)
{
int item;
while (TRUE) { /* Повторять вечно */
if (count == 0) sleepO; /* Если буфер пуст, уйти в состояние ожидания */
item = remove_item(); /* Забрать элемент из буфера */
count = count – 1; /* Уменьшить счетчик элементов в буфере */
if (count == N - 1) wakeup(producer); /* Был ли буфер полон? */
consume_item(item); /* Отправить элемент на печать */
}
}
Для описания на языке С системных вызовов sleep и wakeup мы представили их
в виде вызовов библиотечных процедур. В стандартной библиотеке С их нет, но
они будут доступны в любой системе, в которой присутствуют такие системные вызовы. Процедуры insert_item и remove_item помещают элементы в буфер и извлекают их оттуда.
Теперь давайте вернемся к состоянию состязания. Его возникновение возможно, поскольку доступ к переменной count не ограничен. Может возникнуть следующая ситуация: буфер пуст, и потребитель только что считал значение переменной count, чтобы проверить, не равно ли оно нулю. В этот момент планировщик передал управление производителю, производитель поместил элемент в буфер и увеличил значение count, проверив, что теперь оно стало равно 1. Зная, что перед этим оно было равно 0 и потребитель находился в состоянии ожидания, производитель активизирует его с помощью вызова makeup.
Но потребитель не был в состоянии ожидания, так что сигнал активизации пропал впустую. Когда управление перейдет к потребителю, он вернется к считанному когда-то значению count, обнаружит, что оно равно 0, и уйдет в состояние ожидания. Рано или поздно производитель наполнит буфер и также уйдет в состояние ожидания. Оба процесса так и останутся в этом состоянии.
Суть проблемы в данном случае состоит в том, что сигнал активизации, пришедший к процессу, не находящемуся в состоянии ожидания, пропадает. Если бы не это, проблемы бы не было. Быстрым решением может быть добавление бита ожидания активизации. Если сигнал активизации послан процессу, не находящемуся в состоянии ожидания, этот бит устанавливается. Позже, когда процесс пытается уйти в состояние ожидания, бит ожидания активизации сбрасывается, но процесс остается активным. Этот бит исполняет роль копилки сигналов активизации.
Несмотря на то что введение бита ожидания запуска спасло положение в этом примере, легко сконструировать ситуацию с несколькими процессами, в которой одного бита будет недостаточно. Мы можем добавить еще один бит, или 8, или 32, но это не решит проблему.
Дата публикования: 2015-01-26; Прочитано: 694 | Нарушение авторского права страницы | Мы поможем в написании вашей работы!