Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
1-47.docx
Скачиваний:
17
Добавлен:
01.08.2019
Размер:
1.17 Mб
Скачать
  1. Решение проблемы producer-consumer с помощью семафоров.

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

Producer: while(1) {

produce_item;

put_item;

}

Consumer: while(1) {

get_item;

consume_item;

}

Если буфер заполнен, то производитель должен ждать, пока в нем появится место, чтобы положить туда новую порцию информации. Если буфер пуст, то потребитель должен дожидаться нового сообщения. Как можно реализовать эти условия с помощью семафоров? Возьмем три семафора: empty, full и mutex. Семафор full будем использовать для гарантии того, что потребитель будет ждать, пока в буфере появится информация. Семафор empty будем использовать для организации ожидания производителя при заполненном буфере, а семафор mutex – для организации взаимоисключения на критических участках, которыми являются действия put_item и get_item (операции «положить информацию» и «взять информацию» не могут пересекаться, так как в этом случае возникнет опасность искажения информации). Тогда решение задачи на C-подобном языке выглядит так:

Semaphore mutex = 1;

Semaphore empty = N; /* где N – емкость буфера*/

Semaphore full = 0;

Producer:

while(1) {

produce_item;

P(empty);

P(mutex);

put_item;

V(mutex);

V(full);

}

Consumer:

while(1) {

P(full);

P(mutex);

put_item;

V(mutex);

V(empty);

consume_item;

}

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

  1. Wait-функции и ожидаемые таймеры.

Wait-функции позволяют потоку в любой момент приостановиться и ждать освобождения какого-либо объекта ядра. Из всего семейства этих функций чаще всего используется WaitForSingleObject:

DWORD WaitForSingleObject( HANDLE hObject, DWORD dwMilliseconds);

Когда поток вызывает эту функцию, первый параметр, hObject, идентифицирует объект ядра, поддерживающий состояния «свободен/занят» (То есть любой объект, упомянутый в списке). Второй параметр, dwMilliseconds, указывает, сколько времени (в миллисекундах) поток готов ждать освобождения объекта.

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

WaitForSingleObject(hProcess, INFINITE);

В данном случае константа INFINITE, передаваемая во втором параметре, подсказывает системе, что вызывающий поток готов ждать этого события хоть целую вечность. Именно эта константа обычно и передается функции WaitForSingleObject, но можно указать любое значение в миллисекундах. Разумеется, передача INFINlTE не всегда безопасна. Если объект так и не перейдет в свободное состояние, вызывающий поток никогда не проснется; одно утешение, тратить драгоценное процессорное время он при этом не будет.

Вот пример, иллюстрирующий, как вызывать WaitForSingleObject c значением таймаута, отличным от INFINITE:

DWORD dw = WaitForSlngleObject(hProcess, 5000);

switch (dw) { case WAIT_OBJECT_0: // процесс завершается break;

case WAIT_TIMEOUT: // процесс не завершился в течение 5000 мс break;

case WAIT_FAILED: // неправильный вызов функции (неверный описатель?) break; }

Данный код сообщает системе, что вызывающий поток не должен получать процессорное время, пока не завершится указанный процесс или не пройдет 5000 мс (в зависимости оттого, что случится раньше). Поэтому функция вернет управление либо до истечения 5000 мс, если процесс завершится, либо примерно через 5000 мс, если процесс к тому времени не закончит свою работу. Заметьте, что в параметре dwMilliseconds можно передать 0, и тогда WaitForSingleObject немедленно вернет управление.

Возвращаемое значение функции WaitForSingleObject указывает, почему вызывающий поток снова стал планируемым. Если функция возвращает WAITOBTECT_0, объект свободен, а если WAIT_TIMEOUT — заданное время ожидания (таймаут) истекло. При передаче неверного параметра (например, недопустимого описателя) WaitForSingleObject возвращает WAIT_EAILED. Чтобы выяснить конкретную причину ошибки, можно вызвать функцию GetLastError.

Функция WaitForMultipleObjects аналогична WaitForSingleObject c тем исключением, что позволяет ждать освобождения сразу нескольких объектов или какого-то одного из списка объектов:

DWORD WaitForMultipleObjects(DWOHD dwCount, CONST HANDLE* phObjects, BOOL fWaitAll, DWORD dwMilliseconds);

Параметр dwCount определяет количество интересующих Вас объектов ядра Его значениедолжло быть в пределах от 1 до MAXIMUM_WAIT_OBJECTS (в заголовочных файлах Windows оно определено как 64). Параметр phObject — это указатель на массив описателей объектов ядра.

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

Параметр dwMilliseconds идентичен одноименному параметру функции WaitForSingleObject.

Возвращаемое значение функции WaitForMultipleObjects сообщает, почему возобновилось выполнение вызвавшего ее потока. Значения WAIT_FAILED и WAIT_TIMEOUT никаких пояснений не требуют. Если параметр fWaitAll равен TRUE и все объекты перешли в свободное состояние, функция возвращает значение WAIT_OBJECT_0. Если fWaitAll равен FALSE, она возвращает управление, как только освобождается любой из объектов. Выяснить, какой именно объект освободился можно по возвращаемому значению которое может быть от WAIT_OBJECT_0 до WAIT_OBJECT_0 + dwCount - 1. Иначе говоря, если возвращаемое значение не равно WAIT_TIMEOUT или WAIT_FAILED, из него можно значение WAlT_OBJECT_0, и, в результате, получить индекс в массиве описателей, на который указывает второй параметр функции WaitForMultipleObjects.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]