Содержание - Нижегородский государственный университет

advertisement
Нижегородский Государственный Университет им. Н.И. Лобачевского
Содержание
Общий курс
Теория и практика
параллельных вычислений
•Взаимодействие и взаимоисключение процессов
• Понятие семафоров
• Концепция монитора
• Пример
Лекция 11
Модели функционирования
параллельных программ-2
Гергель В.П.
ННГУ, Н.Новгород, 2001
Взаимодействие и взаимоисключение
процессов…
Алгоритм Деккера. В алгоритме Деккера предлагается объединение
предложений вариантов 1 и 4 решения проблемы взаимоисключения.
Алгоритм Деккера гарантирует корректное решение проблемы
взаимоисключения для двух процессов. Управляющие переменные
ResourceProc1,
ResourceProc1
обеспечивают
взаимоисключение,
переменная ProcessNum исключает возможность бесконечного
откладывания. Если оба процесса пытаются получить доступ к ресурсу,
то процесс, номер которого указан в ProcessNum, продолжает проверку
. цикл ожидания ресурса).
возможности доступа к ресурсу (внешний
Другой же процесс в этом случае снимает свой запрос на ресурс,
ожидает своей очереди доступа к ресурсу (внутренний цикл ожидания) и
возобновляет свой запрос на ресурс.
Алгоритм Деккера может быть обобщен на случай произвольного
количества процессов, однако такое обобщение приводит к заметному
усложнению выполняемых действий. Кроме того, программное решение
проблемы взаимоисключения процессов. приводит к нерациональному
использованию процессорного времени ЭВМ (процессу, ожидающему
освобождения ресурса, постоянно требуется процессор для проверки
возможности продолжения – активное ожидание (busy wait)).
int ProcessNum=1; // номер процесса для доступа к ресурсу
int ResourceProc1 = 0; // =1 – ресурс занят процессом 1
int ResourceProc2 = 0; // =1 – ресурс занят процессом 2
Process_1() {
while (1) {
ResourceProc1=1; // процесс 1 пытается занять ресурс
while ( ResourceProc2 == 1 ) {/* цикл ожидания доступа к ресурсу */
if ( ProcessNum == 2 ) {
ResourceProc1 = 0;
// повторять, пока ресурс занят процессом 2
while ( ProcessNum == 2 ); ResourceProc1 = 1;
}}
< Использование общего ресурса >
ProcessNum = 2; ResourceProc1 = 0;
}}
Process_2() {
while (1) {
ResourceProc2=1; // процесс 2 пытается занять ресурс
while ( ResourceProc1 == 1 ) {/* цикл ожидания доступа к ресурсу */
if ( ProcessNum == 1 ) {
ResourceProc2 = 0;
// повторять, пока ресурс используется процессом 1
while ( ProcessNum == 1 ); ResourceProc2 = 1;
}}
< Использование общего ресурса >
ProcessNum = 1; ResourceProc2 = 0;
}}
.
.
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
ННГУ, Н.Новгород, 2001
11.3
Понятие семафоров…
.
11.2
Взаимодействие и взаимоисключение
процессов…
.
.
Параллельные вычисления
@ Гергель В.П.
Параллельные вычисления
@ Гергель В.П.
11.4
Понятие семафоров …
Семафоры Дейкстры. Под семафором S обычно понимается переменная
особого типа, значение которой может опрашиваться и изменяться только
при помощи специальных операций P(S) и V(S), реализуемых в
соответствии со следующими алгоритмами:
• операция P(S)
если S>0
то S = S – 1
.
иначе < ожидать S >
• операция V(S)
если < один или несколько процессов ожидают S >
то < снять ожидание у одного из ожидающих
процессов >
иначе S = S + 1
Принципиальным в понимании семафоров является то, что операции P(S)
и V(S) предполагаются неделимыми, что гарантирует взаимоисключение
при использование общих семафоров (для. обеспечения неделимости
операции обслуживания семафоров обычно реализуются средствами
операционной системы).
Различают два основных типа семафоров. Двоичные семафоры принимают
только значения 0 и 1, область значений общих семафоров - целые
неотрицательные значения.
Семафоры позволяют упростить решение проблем синхронизации и
взаимоисключения процессов. Так, например, проблема взаимоисключения
при помощи семафоров может иметь следующее простое решение.
Semaphore Mutex=1; // семафор взаимоисключения процессов
Process_1() {
while (1) {
// проверить семафор и ждать, если ресурс занят
P(Mutex);
< Использование общего ресурса >
// освободить один из ожидающих ресурса процессов
// увеличить семафор, если нет ожидающих процессов
V(Mutex);
.
.
}}
Process_2() {
while (1) {
// проверить семафор и ждать, если ресурс занят
P(Mutex);
< Использование общего ресурса >
// освободить один из ожидающих ресурса процессов
// увеличить семафор, если нет ожидающих процессов
V(Mutex);
.
}}
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
ННГУ, Н.Новгород, 2001
11.5
Параллельные вычисления
@ Гергель В.П.
11.6
1
Пример: Задача "производительпотребитель"…
.
Пример: Задача "производительпотребитель"…
Задача "производитель-потребитель" (producerconsumer)
является классической проблемой, на примере которой
происходит демонстрация приемов и методов
.
взаимоисключения параллельных процессов:
• Производитель производит продукцию и передает ее
для хранения в хранилище (контроль переполнения)
• Потребитель потребляет продукцию, располагаемую в
хранилище (учет ситуации отсутствия продукции)
Возможная дополнительная интерпретация – "писатель.
читатель", очередь сообщений, буферизация
данных
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
Вариант 1. Неограниченный буфер
.
.
.
ННГУ, Н.Новгород, 2001
11.7
Пример: Задача "производительпотребитель"…
.
/*
/*
/*
/*
int Buf[];
int Count=0, PutIndex=0, GetIndex=0;
Semaphore
n=0; /* семафор – количество записей
*/
BinSemaphore s=1; /* двоичный семафор – взаимоисключение */
.
.
/* получить следующую позицию */
int GetNextIndex ( int Index ) { return ++Index; }
.
}
/* прочитать из буфера */
int Get ( void ){
P(n);
int Value = Buf[GetIndex];
GetIndex = GetNextIndex(GetIndex); Count--;
return Value;
.
Параллельные вычисления
@ Гергель В.П.
.
@ Гергель В.П.
11.9
Пример: Задача "производительпотребитель"…
int Buf[NMAX];
int Count=0, PutIndex=0, GetIndex=0;
Semaphore
n=0;
/* семафор – количество записей
*/
BinSemaphore s=1;
/* двоичный семафор – взаимоисключение
*/
Semaphore
e=NMAX; /* семафор – количество свободных позиций */
/* получить следующую позицию */
int GetNextIndex ( int Index ) { return (Index+1) % NMAX; }
void Put ( int Value ){ /* записать в буфер */
P(e);
Buf[PutIndex] = Value;
PutIndex = GetNextIndex(PutIndex);
P(s); Count++; V(s);
V(n);
.
.
}
int Get ( void ){ /* прочитать из буфера */
P(n);
int Value = Buf[GetIndex];
GetIndex = GetNextIndex(GetIndex);
P(s); Count--; V(s);
V(e);
return Value;
.
}
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
11.10
Концепция монитора…
Вариант 4. Ограниченный буфер – учет переполнения
.
/* получить следующую позицию */
int GetNextIndex ( int Index ) { return ++Index; }
void Put ( int Value ){ /* записать в буфер */
Buf[PutIndex] = Value;
PutIndex = GetNextIndex(PutIndex);
P(s); Count++; V(s);
V(n);
}
int Get ( void ){ /* прочитать из буфера */
P(n);
int Value = Buf[GetIndex];
GetIndex = GetNextIndex(GetIndex);
P(s); Count--; V(s);
return Value;
}
Параллельные вычисления
ННГУ, Н.Новгород, 2001
/* записать в буфер */
void Put ( int Value ){
Buf[PutIndex] = Value;
PutIndex = GetNextIndex(PutIndex); Count++;
V(n);
ННГУ, Н.Новгород, 2001
11.8
Вариант 3. Взаимоисключение обработки общей
переменной Count
количество записей в буфере */
позиция записи в буфер
*/
позиция чтения из буфера
*/
семафор – количество записей*/
}
Параллельные вычисления
@ Гергель В.П.
Пример: Задача "производительпотребитель"…
Вариант 2. Учет ситуации пустого буфера
int Buf[];
int Count=0;
int PutIndex=0;
int GetIndex=0;
Semaphore n=0;
int Buf[];
int Count=0;
/* количество записей в буфере */
int PutIndex=0; /* позиция записи в буфер
*/
int GetIndex=0; /* позиция чтения из буфера
*/
/* получить следующую позицию */
int GetNextIndex ( int Index ) { return ++Index; }
/* записать в буфер */
void Put ( int Value ){
Buf[PutIndex] = Value;
PutIndex = GetNextIndex(PutIndex);
Count++;
}
/* прочитать из буфера */
int Get ( void ){
int Value = Buf[GetIndex];
GetIndex = GetNextIndex(GetIndex);
Count--;
return Value;
}
Под монитором (Hansen, 1973; Hoare, 1974) в рамках
контекста проблемы взаимоисключения понимается
программная конструкция, объединяющая в своем
составе разделяемые переменные, для которых должно
.
обеспечиваться взаимоисключение, и процедуры для
обработки этих разделяемых переменных. При этом:
• Доступ к разделяемым переменным возможен только
через процедуры монитора;
• Для вызова процедур монитора
. обеспечивается
взаимоисключение.
ННГУ, Н.Новгород, 2001
11.11
Параллельные вычисления
@ Гергель В.П.
11.12
2
Концепция монитора…
.
Концепция монитора…
Для синхронизации действий, выполняемых
монитором, вводится новый тип управляющих данных –
условий (condition). Для переменных типа условия
возможны только два воздействия:
.
• wait(c) – блокировка процесса, из которого было
выполнено обращение к функции wait и постановка
этого процесса в очередь процессов, ожидающих
данного условия;
• signal(c) – снятие блокировки .для наиболее давно
блокированного процесса в очереди ожидания данного
условия (дисциплина FIFO).
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
Пример: Реализация ограниченного буфера при помощи
использования монитора (вариант 5)
.
.
}
int Get ( void ){ /* прочитать из буфера */
if ( Count == 0 ) wait(nonEmpty);
int Value = Buf[GetIndex];
GetIndex = GetNextIndex(GetIndex);
Count--;
signal(nonFull);
return Value;
.
}
ННГУ, Н.Новгород, 2001
.
.
Параллельные вычисления
@ Гергель В.П.
Параллельные вычисления
@ Гергель В.П.
11.14
Пример: Задача "обедающие
философы"…
Достоинства подхода:
• Централизация решения вопросов взаимоисключения;
• Исключения критических секций из состава процессов;
• Повышение уровня средств взаимоисключения
.
процессов
ННГУ, Н.Новгород, 2001
}
11.13
Концепция монитора
.
monitor BufferManager {
int Buf[NMAX];
int Count=0, PutIndex=0, GetIndex=0;
condition nonFull, nonEmpty;
/* получить следующую позицию */
int GetNextIndex ( int Index ) { return (Index+1) % NMAX; }
void Put ( int Value ){ /* записать в буфер */
if ( Count == NMAX ) wait(nonFull);
Buf[PutIndex] = Value;
PutIndex = GetNextIndex(PutIndex);
Count++;
signal(nonEmpty);
Одна из классических задач для демонстрации проблемы
взаимоисключения параллельных процессов (Дейкстра)
• Имеется локально-проживающее сообщество философов, основное
занятие которых – размышление (think);
• Периодически (в случайные моменты времени) философы
испытывают чувство голода и отправляются
в общую столовую для
.
питания (eat);
• В столовой для кормления философов используется один круглый
стол; вокруг стола места для питания (число мест совпадает с
количеством философов);
• Место для питания – стол и тарелка на столе; между двумя
соседними тарелками располагается одна вилка;
• Для еды философ обязательно использует две вилки (слева и справа
.
от места питания);
• Время питания является конечным.
ННГУ, Н.Новгород, 2001
11.15
Параллельные вычисления
@ Гергель В.П.
11.16
Пример: Задача "обедающие
философы"…
Пример: Задача "обедающие
философы"…
Вариант 1: Использование семафоров для каждой вилки
.
.
Philisopher() {
think();
eat();
}
.
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
.
BinSemaphore fork[5] = { 1,1,1,1,1 };
eat() {
int i;
.
/* выбор места для питания */
i=…;
P(fork[i]);
P(fork[(i+1)%5]);
/* прием пищи */
V(fork[i]);
.
V(fork[(i+1)%5]);
}
ННГУ, Н.Новгород, 2001
11.17
Параллельные вычисления
@ Гергель В.П.
11.18
3
Пример: Задача "обедающие
философы"…
Пример: Задача "обедающие
философы"…
Вариант 3: Полное решение (семафоры) – одновременно в
столовой может быть не более 4 философов
Вариант 2: Использование монитора
monitor DiningPhilosophers {
int ForkNum[5] = { 2,2,2,2,2 }; /* количество свободных вилок */
conditon PlaceReady[5];
void TakePlace(int i) {
if ( fork[i] != 2 ) wait(PlaceReady[i]);
ForkNum[i] = 0;
ForkNum[(i-1)%5]--;
ForkNum[(i+1)%5]--;
}
void ReleasePlace(int i) {
ForkNum[i] = 2;
ForkNum[(i-1)%5]++;
ForkNum[(i+1)%5]++;
if ( ForkNum[i] == 2 ) signal(PlaceReady[i]);
if ( ForkNum[(i-1)%5] == 2 ) signal(PlaceReady[(i-1)%5]);
if ( ForkNum[(i+1)%5] == 2 ) signal(PlaceReady[(i+1)%5]);
.
.
}
.
}
eat() {
int i;
i=…; /* выбор места для питания */
TakePlace(i);
/* прием пищи */
ReleasePlace(i);
}
ННГУ, Н.Новгород, 2001
.
Параллельные вычисления
@ Гергель В.П.
BinSemaphore fork[5] = { 1,1,1,1,1 };
Semaphore
room=4;
/* room - количество философов в столовой */
eat() {
.
int i;
/* места распределены между философами
*/
/* каждый философ имеет свое постоянное место */
i=…;
P(room);
P(fork[i]);
P(fork[(i+1)%5]);
.
/* прием пищи */
V(fork[i]);
V(fork[(i+1)%5]);
V(room);
}
ННГУ, Н.Новгород, 2001
11.19
Пример: Задача "обедающие
философы"…
.
Параллельные вычисления
@ Гергель В.П.
Вариант 4: Полное решение (монитор) – одновременно в столовой может
быть не более 2 философов
}
void TakePlace(int i) {
if ( fork[i] != 2 ) wait(PlaceReady[i]);
ForkNum[i] = 0;
ForkNum[(i-1)%5]--;
ForkNum[(i+1)%5]--;
Count+;
}
void ReleasePlace(int i) {
ForkNum[i] = 2;
ForkNum[(i-1)%5]++;
ForkNum[(i+1)%5]++;
if ( ForkNum[i] == 2 ) signal(PlaceReady[i]);
if ( ForkNum[(i-1)%5] == 2 ) signal(PlaceReady[(i-1)%5]);
if ( ForkNum[(i+1)%5] == 2 ) signal(PlaceReady[(i+1)%5]);
Count--;
signal(Room);
.
}}
ННГУ, Н.Новгород, 2001
11.21
Параллельные вычисления
@ Гергель В.П.
11.22
Пример: "Спящий парикмахер"
.
.
.
.
Параллельные вычисления
@ Гергель В.П.
monitor DiningPhilosophers {
int ForkNum[5] = { 2,2,2,2,2 };
conditon PlaceReady[5];
conditon Room;
int Count = 0; /* количество обедающих философов */
int GetPlaceNum() { /* метрдотель */
if ( Count == 2 ) wait(Room);
for ( int i=0; i<5; i++ )
if ( ForkNum[i] == 2 ) return i;
return –1;
.
.
Пример: "Спящий парикмахер"…
ННГУ, Н.Новгород, 2001
11.20
Пример: Задача "обедающие
философы"
Утверждение 1: Если Pi выполняет wait(fork[i]), блокировка этого
процесса не может быть бесконечной.
Д-во: Если процесс Pi блокирован семафором fork[i], это означает,
что i вилка занята философом Pi-1. Но Pi-1 рано или поздно завершит
прием пищи и освободит вилку i. Как результат, процесс Pi будет
разблокирован.
.
ожидает wait(fork[i+1]),
Утверждение 2: Если Pi ожидает бесконечно
тогда Pi+1 ожидает бесконечно ожидает wait(fork[i+2]).
Д-во: Если процесс Pi блокирован бесконечно семафором fork[i],
это означает, что i вилка занята философом Pi-1. Но бесконечное
занятие вилки может быть только в случае блокировки Pi+1 из-за
занятости вилки i+1 (т.е. из-за wait(fork[i+2])).
Утверждение 3: Если Pi выполняет wait(fork[i+1]), блокировка этого
процесса не может быть бесконечной.
.
Д-во: Бесконечная блокировка невозможна в силу ограниченности
количества философов в столовой (4) и условия блокировки из-за
wait(fork[i+1]) (см. Лемму 2).
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
Самостоятельное задание.
• В парикмахерской 2 помещения: рабочая комната и
комната для ожидания
• Парикмахер обслуживает клиента, провожает его к
выходной двери, затем приглашает
клиента
. очередного
из комнаты для ожидания; если клиентов
нет,
парикмахер садится в кресло и засыпает
• Клиент заходит в комнату для ожидания;
- если в комнате уже есть другие клиенты, новый
посетитель занимает очередь и ожидает;
- если комната для ожидания пуста, новый клиент
.
заглядывает в рабочую комнату
и, если парикмахер
занят, остается в комнате для ожидания
- Если парикмахер спит, то посетитель переходит в
рабочую комнату и будит парикмахера
ННГУ, Н.Новгород, 2001
11.23
Параллельные вычисления
@ Гергель В.П.
11.24
4
Вопросы для обсуждения
• Сравнение способов взаимоисключения:
семафоры и мониторы
ННГУ, Н.Новгород, 2001
Параллельные вычисления
@ Гергель В.П.
• Решение иллюстративной задачи
взаимоисключения "спящий парикмахер"
ННГУ, Н.Новгород, 2001
11.25
Заключение
Параллельные вычисления
@ Гергель В.П.
Параллельные вычисления
@ Гергель В.П.
11.26
Следующая тема
• Проблема взаимодействия и
взаимоисключения процессов
• Семафоры
• Мониторы
• Иллюстративные задачи для проблемы
взаимоисключения
ННГУ, Н.Новгород, 2001
Задания для самостоятельной
работы
• Модели функционирования
параллельных программ – 3
ННГУ, Н.Новгород, 2001
11.27
Параллельные вычисления
@ Гергель В.П.
11.28
5
Download