Нижегородский государственный университет им. Н.И.Лобачевского
Опубликован: 02.10.2012 | Доступ: свободный | Студентов: 1741 / 191 | Длительность: 17:47:00
Специальности: Программист
Лекция 3:

Операционные системы - аспекты параллелизма

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >

3.6.2. Задача "Читатели-Писатели"

Вторая типовая задача – проблема читателей и писателей (Readers-Writers problem). Предположим, у нас есть хранилище данных, с которым работают несколько потоков. Потоки могут выполнять операции чтения и записи данных.

Несколько потоков, обращающихся к базе данных

Рис. 3.9. Несколько потоков, обращающихся к базе данных

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

Writer:
while(true) {
PrepareData(); // Подготовить данные
Write(Data); // Записать данные
}
Reader:
while(true) {
Read(&Data); // Прочитать данные
UseData(); // Использовать данные
}

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

Задача "Читатели-Писатели" заключается в обеспечении согласованного доступа нескольких потоков к разделяемым данным. Корректное решение должно удовлетворять следующим условиям:

  • потоки выполняются параллельно;
  • во время выполнения потоком операции записи, данные не могут использоваться другими потоками;
  • во время выполнения потоком операции чтения, другие потоки также могут выполнять операцию чтения;
  • потоки должны завершить работу в течение конечного времени.

Решение задачи "Писатели-Читатели", использующее семафоры

Semaphore RC = 1; //управляет доступом к переменной ReadCount
Semaphore Access = 1; //управляет доступом к данным
int ReadCount = 0; //количество "активных"(читающих) читателей
Writer(){
P(Access); // захватываем доступ к критическим данным
<выполняем операцию записи>
V(Access); // освобождаем доступ к критическим данным
}
Reader(){
P(RC);//получаем эксклюзивный доступ к переменной ReadCount
ReadСount++; // увеличиваем число активных читателей
if( ReadСount == 1 )
P(Access); // захватываем доступ к критическим данным
V(RC);// освобождаем доступ к переменной ReadCount
<выполняем операцию чтения>
P(RC);//получаем эксклюзивный доступ к переменной ReadCount
61
ReadСount--; // уменьшаем число активных читателей
if( ReadСount == 0 )
V(Access); // освобождаем доступ к критическим данным
V(RC);// освобождаем доступ к переменной ReadCount
}

Для защиты разделяемых критических данных используется двоичный семафор Access. Функция писателя просто получает доступ к критическим данным (уменьшая семафор Access) и использует их.

Функция читателя должна обеспечить одновременный доступ к критическим данным нескольких читателей, поэтому она использует дополнительную переменную ReadCount – количество читателей, выполняющих операцию чтения в настоящий момент. Данная переменная также требует защиты для корректного использования несколькими читателями (для этого введен двоичный семафор RC), но ее использование позволяет выполнять запрос на доступ к критическим данным только первому читателю (остальные будут заблокированы при попытке уменьшить семафор RC), а освобождение данных оставить последнему читателю, завершившему операцию чтения. Обратим внимание, что вследствие подобного подхода функция читателя содержит 3 критических секции: одну, связанную с чтением критических данных, и две, связанных с изменением переменной ReadCount и организации процесса входа и выхода из первой критической секции.

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

3.6.3. Задача "Обедающие философы"

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

Оригинальная постановка задачи, предложенная Э. Дийкстрой, звучит следующим образом. На круглом столе расставлены тарелки, по одной на каждого философа. В центре стола – большое блюдо спагетти, а на столе лежат пять вилок— каждая между двумя соседними тарелками. Каждый философ находится только в двух состояниях— либо он размышляет, либо ест спагетти. Начать думать после еды философу ничто не мешает. Но чтобы начать есть, необходимо выполнить ряд условий. Предполагается, что любой философ, прежде чем начать есть, должен положить из общего блюда спагетти себе в тарелку. Для этого он одновременно должен держать в левой и правой руках по вилке, набрать спагетти в тарелку с их помощью и, не выпуская вилок из рук, начать есть. Закончив еду, философ кладет вилки слева и справа от своей тарелки и опять начинает размышлять до тех пор, пока снова не проголодается.

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

Потоки, конкурирующие за множества ресурсов

Рис. 3.10. Потоки, конкурирующие за множества ресурсов

Функциональность потоков-философов можно записать следующим образом.

Philosopher:
while(true) {
think(); // Размышляем
take_forks(); // Берем 2 вилки
eat(); // Обедаем
put_forks(i); // Кладем на стол обе вилки
}

Задача "Обедающие философы" заключается в обеспечении согласованного доступа нескольких потоков к разделяемым ресурсам. Корректное решение должно удовлетворять следующим условиям:

  • потоки выполняются параллельно;
  • во время использования "вилок" потоком данные "вилки" не должны использоваться его соседями;
  • потоки должны завершить работу в течение конечного времени.

Решение задачи "Обедающие философы", использующее семафоры (предполагается количество философов, равное 5)

#define LEFT (i+4)%5
#define RIGHT (i+1)%5
#define THINKING 0 // Состояния философов
#define HUNGRY 1
#define EATING 2
int State[5]; //Массив состояний философов
Semaphore Access=1; //Управление доступом к критическим данным
Semaphore S[5]={0,0,0,0,0};//Для ожидания освобождения ресурсов
void Philosopher( int i ){ // i – номер философа от 0 до 4
while( true ){
think(); // Размышляем
take_forks(i); // Берем 2 вилки или блокируемся
eat(); // Обедаем
put_forks(i); // Кладем на стол обе вилки
}
}
void take_forks( int i){
P(Access); // Захватываем доступ к критическим данным
State[i] = HUNGRY; // Изменяем свое состояние
test(i); // Пытаемся взять две вилки
V(Access); // Освобождаем доступ к критическим данным
P(S[i]); // Блокируемся, если не удалось взять вилки
}
void put_forks( int i){
P(Access); // Захватываем доступ к критическим данным
State[i] = THINKING; // Изменяем свое состояние
test(LEFT); // Пытаемся накормить соседа слева
test(RIGHT); // Пытаемся накормить соседа справа
V(Access); // Освобождаем доступ к критическим данным
}
void test( int i ){
if(state[i]==HUNGRY&&state[LEFT]!=EATING&&state[RIGHT]!=EATING){
State[i] = EATING; // Изменяем состояние
V(S[i]); // Разрешаем перейти к еде
}
}

Семафор Access используется для защиты массива состояний философов State[5].

Семафоры S[5] используются для оповещения философа о возможности взять две вилки и начать обед. Отметим, что момент начала обеда определяется не философом, желающим обедать, а философом, завершившим обедать.

Можно предположить следующие типичные недостатки, присущие различным решениям задачи об обедающих философах:

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

Имеется простое расширение предложенного решения на случай, когда каждый поток может использовать произвольное множество ресурсов. Для этого, во-первых, необходимо добавить массив признаков занятости ресурсов и описания множеств ресурсов, затребованных каждым потоком, во-вторых, при освобождении ресурсов поток должен просматривать все потоки, ожидающие освобождения ресурсов, и в случае, если запросы одного или нескольких потоков могут быть удовлетворены, резервировать за ними ресурсы и разрешать им продолжить выполнение.

Подобное решение будет лишено первого недостатка (по причине того, что все потоки требуют все необходимые им ресурсы сразу, а впоследствии их все возвращают), но будет подвержено второму, как и оригинальное решение.

7. Взаимоблокировка

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

Предположим, что работа процесса с ресурсами включает три стадии: получить/захватить ресурс (если ресурс свободен, он предоставляется процессу; если ресурс занят, процесс блокируется), использовать ресурс, освободить ресурс. Тогда при наличии двух процессов (A и B) и двух ресурсов (1-й и 2-й) возможна следующая нежелательная ситуация: процесс A захватил ресурс 1 и ожидает предоставления ему ресурса 2, процесс B захватил ресурс 2 и ожидает предоставления ему ресурса 1. Такая ситуация называется взаимоблокировкой или тупиком (deadlock).

Пример кода, который может привести к взаимоблокировке

Semaphore Sem1 = 1;
Semaphore Sem2 = 1;
Код процесса A:
P(Sem1);
P(Sem2);
/*использование ресурсов*/
V(Sem2);
V(Sem1);
Код процесса B:
P(Sem2);
P(Sem1);
/*использование ресурсов*/
V(Sem2);
V(Sem1);

Если после уменьшения семафора Sem1 потоком процесса A центральный процессор будет передан потоку процесса B, который уменьшит семафор Sem2, мы получим взаимоблокировку.

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

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

Далее изложены основные аспекты проблемы взаимоблокировки: обнаружение, предотвращение, избегание и восстановление после возникновения.

< Лекция 2 || Лекция 3: 123456 || Лекция 4 >
Дмитрий Остапенко
Дмитрий Остапенко

поддерживаю выше заданые вопросы

 

Павел Каширин
Павел Каширин

Скачал архив и незнаю как ничать изучать материал. Видео не воспроизводится (скачено очень много кодеков, различных плееров -- никакого эффекта. Максимум видно часть изображения без звука). При старте ReplayMeeting и Start в браузерах google chrome, ie возникает script error с невнятным описанием. В firefox ситуация еще интереснее. Выводится: 

Meet Now: Кукаева Светлана Александровна. 

Meeting Start Time: 09.10.2012, 16:58:04
Meeting Stop Time: 09.10.2012, 18:45:18
Recording Duration:01:47:14

Downloading...

Your Web browser is not configured to play Windows Media audio/video files.

Make sure the features are enabled and available.