МИНИСТЕРСТВО ОБРАЗОВАНИЯ РФ Восточно-Сибирский государственный технологический университет
Степанов Б.М.
Основные зада...
15 downloads
245 Views
224KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
МИНИСТЕРСТВО ОБРАЗОВАНИЯ РФ Восточно-Сибирский государственный технологический университет
Степанов Б.М.
Основные задачи параллельного программирования Конспект лекций по дисциплине «Параллельное программирование» для студентов специальности 220400 «Программное обеспечение ВТ и АС»
Издательство ВСГТУ Улан-Удэ, 2001
УДК 681.142 Основные задачи параллельного программирования: Конспект лекций/ Сост. Степанов Б.М. – Улан-Удэ, Изд-во ВСГТУ, 2001.- 30 с. Конспект лекций предназначен для студентов специальности 220400 и 351500 и содержит теоретический материал по дисциплине «Параллельное программирование», в котором изложены основные понятия параллельного программирования и классические задачи распараллеливания вычислительных процессов, решение которых позволяет повысить эффективность вычислительной системы.
Рецензент: Бильгаева Н.Ц., к.т.н., доц. ВСГТУ
Печатается по решению редакционно-издательского совета ВСГТУ ВСГТУ, 2001 г.
2
Оглавление Введение............................................................................................................................... 4 Лекция 1. Основные понятия и определения............................................................... 5 Лекция 2. Формулировка задачи взаимного исключения......................................... 7 Лекция 3. Средства взаимного исключения............................................................... 10 Использование специальных команд .......................................................................... 10 Лекция 4. Решение задачи взаимного исключения с помощью семафоров......... 12 Лекция 5. Другие определения семафоров.................................................................. 13 Лекция 6. Задача “поставщик – потребитель”........................................................... 15 Лекция 7. Задача синхронизации процессов .............................................................. 16 Список рекомендуемой литературы ............................................................................ 18
3
Введение Большинство программ пишется для последовательного выполнения. Однако для достижения более высокой производительности создаются мультипроцессорные вычислительные системы. Мультипроцессорная архитектура может явиться средством значительного увеличения мощности вычислительных установок. Важным качеством мультипроцессорных комплексов является их гибкость, их модульная архитектура позволяет легко увеличивать вычислительные мощности путем подключения при необходимости дополнительных процессоров. Мультипроцессорные комплексы позволяют воспользоваться преимуществами параллелизма. Параллелизм в программах может быть явный или неявный (скрытый). Явный параллелизм программист указывает в своей программе при помощи специальных конструкций. Тогда система каждую специальную конструкцию может выполнять на отдельном процессоре. Явное указание параллелизма налагает определенную ответственность на программиста. Данный конспект лекций дает понимание целей параллельного программирования, содержит описание механизмов и алгоритмов распараллеливания процессов, а также описание специальных конструкций, позволяющих программировать параллельные процессы.
4
Лекция 1. Основные понятия и определения Процессы являются параллельными, если они существуют (выполняются) одновременно. Параллельные процессы могут работать совершенно независимо друг от друга или же они могут периодически синхронизироваться и взаимодействовать. Условимся изображать выполнение параллельных процессов в виде направленного графа, дуги которого помечаются именами процессов как показано на рис.1. P P
P P
P
P P Рис.1
Мы будем рассматривать только последовательно-параллельные процессы, то есть такие процессы, которые могут быть разбиты только на последовательные и параллельные участки. Приведённый граф на рис.1 не является последовательно-параллельным. На рис.2 приведен пример последовательно-параллельного графа. P1 P2
P3
P5 P6
P7
P4
P8 P9 Рис.2 Существует два основных способа описания последовательно-параллельных процессов: 1) использование специального разделителя, соединяющего параллельно выполняемые процессы; 2) использование специальных операторных скобок для объединения параллельных процессов. Например, в первом случае можно считать символ «;» (точка с запятой) указателем последовательного выполнения процесса, а символ «||»– указателем параллельного выполнения процессов. Тогда граф можно представить так: P1; P2 || P3; P4 || begin P5; P6 || P7; P8 end ; P9
5
Во втором случае используют скобки begin end , как обозначающие последовательное выполнение операций, а parbegin и parend (или cobegin и coend) для обозначения параллельно выполняющихся процессов. Тогда приведенный выше граф запишется следующим образом: P1; cobegin P2; P3 coend ; cobegin P4; cobegin P5; cobegin P6; P7 coend ; P8 coend ; P9 Как отмечалось выше, параллельные процессы могут быть независимыми и взаимодействующими. Если процессы P1 и P2 – независимые, то они работают с непересекающимися множествами переменных. Если множества переменных, с которыми работают процессы P1 и P2 являются пересекающимися (общими), то процессы взаимодействующие. Наличие общих разделяемых переменных приводит к тому, что взаимодействующие процессы могут влиять на работу друг друга. Термин “переменная” в этих определениях является не совсем точным. В общих случаях можно говорить о файлах, областях памяти, и т. д. Выделим два способа взаимодействия и, в зависимости от них, будем говорить о: конкурирующих процессах; о процессах, выполняющих совместную работу. Как типичный случай для конкурирующих процессов можно привести пример, когда процессы P1 и P2 хотят выполнить запись блока данных в одну и ту же область памяти. Для процессов, выполняющих совместную работу характерна ситуация, когда процесс Р1 формирует блок данных и заносит его в буфер, а процесс Р2 читает данные из буфера. Рассмотрим более подробно пример для конкурирующих процессов. Допустим, что имеется два параллельных процесса и имеется счётчик, с которым работают эти процессы. Счётчик должен подсчитывать количество обращений к нему (рис.3). Процессы считывают состояние счётчика каждый в свою локальную переменную (Счет1 и Счет2), изменяют ее, а затем записывают значение в счетчик. Один из вариантов работы приведен в таблице 1. Допустим, что исходное состояние счётчика было 0, следовательно, не выполняется подсчет количества обращений к переменной Счет, поскольку действия не синхронны. То есть, в приведённом примере получается неправильное значение счётчика за счёт того, что операции записи и чтения разных процессов чередуются. Таким образом, работа конкурирующих процессов может приводить к неправильному результату. Счетчик
Процесс 1
Процесс 2 Рис.3 Таблица 1
Время 1 2 3 4 5 6
Процесс ПРОЦЕСС 1 ПРОЦЕСС 1 ПРОЦЕСС 2 ПРОЦЕСС 1 ПРОЦЕСС 2 ПРОЦЕСС 2
Действие Счет1:=Счетчик Счет1:=Счет+1 Счет2:=Счетчик Счетчик:=Счет1 Счет2:=Счет2+1 Счетчик:=Счет2
6
Значение 0 1 0 1 1 1
Рассмотренные примеры показывают трудности, с которыми приходится сталкиваться при работе с конкурирующими процессами (решение задачи взаимного исключения). Эту задачу можно решить с использованием большого числа вспомогательных переменных. Использование вспомогательных переменных приводит к достаточно запутаным и мало наглядным решениям. Чтобы при работе с разделяемыми переменными не возникало ошибок, необходимо предотвратить одновременный доступ к этим переменным. Это запрещение одновременного доступа называется взаимным исключением конкурирующих процессов. Задача взаимного исключения – главная задача параллельного программирования. Лекция 2. Формулировка задачи взаимного исключения Критический интервал (секция, участок) – это последовательность операторов заданного процесса, выполняющих действия с разделяемыми переменными. Правильное выполнение конкурирующих процессов будет происходить в том случае, когда критические интервалы взаимно исключаются (то есть одновременно не выполняются). Если процесс выполняет операторы своего критического интервала, то будем считать, что он находится в критическом интервале. Взаимное исключение критических интервалов должно выполняться согласно следующим правилам: 1) в каждый момент времени только один процесс может находиться внутри своего критического интервала; 2) ни один процесс не должен оставаться неограниченно долго внутри своего критического интервала; 3) никакой процесс, находящийся вне своего критического интервала, не должен препятствовать другому процессу войти в его критический интервал. Средства взаимного исключения Для решения задачи взаимного исключения применяются следующие способы: 1) Блокировка памяти запрещает одновременный доступ к одной и той же ячейке памяти двум различным устройствам. Это средство относится к аппаратным решениям. Здесь чередование обращений не учитывается. Если для выполнения критического интервала требуется более одной команды, то этого средства оказывается недостаточно. 2) Использование вспомогательных переменных для контроля нахождения процессов в их критических интервалах. 3) Использование специальных команд “Проверить и установить” – Test & Set (Tset – в микропроцессоре Z80, XСHG в процессорах 80х86). Рассмотрим использование вспомогательных переменных. А) Использование одной вспомогательной переменной. В этом варианте решения задачи взаимного исключения переменная используется для задания очередности выполнения процессами критических интервалов. Допустим, что рассматривается два процесса. Вспомогательная переменная может быть булевского типа. Если её значение “true”, то процесс1 находится в критическом интервале, если ее значение ‘‘false’’, то процесс2- в критическом интервале. Алгоритм решения приведен ниже. Var b: boolean; Begin 7
b:= true; Cobegin Процесс 1: begin Начальная часть процесса 1 while not b do; Критический интервал 1, b:= false; Оставшаяся часть процесса 1 End;{Процесс 1} Процесс 2: Begin Начальная часть процесса 2 while b do ; Критический интервал 2; b:= true; Оставшаяся часть процесса 2 End;{Процесс 2} Coend; End; Для анализа данного решения рассмотрим временную диаграмму, приведенную на рис.4. В общем случае предполагается, что нам ничего не известно о скоростях протекания процессов и о времени их протекания. Допустим, что “процесс 1” – быстрый, а “процесс 2”– медленный. В рассматриваемом случае взаимного исключения возникает ситуация, когда процесс 2 находится вне критического интервала и препятствует процессу 1 войти в свой критический интервал. То есть, с общей позиции, это решение неудовлетворительно. Другое дело, если процессы короткие (то есть, знаем скорость протекания процессов.)
Процесс1
Н КИ
КИ
КИ Процесс2
TRUE
FALSE
TRUE
Рис.4 Б) Использование двух вспомогательных переменных, показывающих, какой из процессов находится в критическом интервале. Var b1, b2 : boolean; Begin b1 := false; b2 := false; {b1– процесс 1 не находится в критическом интервале, b2 – процесс 2 не находится в критическом интервале} cobegin процесс 1: begin 8
while b2 do ; b1:= true; Критический интервал 1; b1:=false; Оставшаяся часть процесса; end; процесс 2: begin while b1 do ; b2:= true; Критический интервал 2; b2:=false; Оставшаяся часть процесса; end; coend; Условие 3 взаимного исключения выполняется, но здесь возможны два случая: 1. Процессы входят одновременно в свои критические интервалы, при этом b1:=false, b2:=false, следовательно, оба процесса изменят значения переменных b1 и b2 на true, и ни один, ни другой не сработают , то есть это решение не отвечает 1-му правилу взаимного исключения. 2. Допустим, что процессы входят не одновременно в свои критические интервалы. Процессы обладают разными скоростями. В этом случае: процесс 1 проверил условие b2, но ещё не успел изменить значение b1, вышедши из цикла. Если в это время процесс 2 осуществит проверку b1 (которая является false), то он сможет войти в свой критический интервал без препятствий. Приведённое решение является неудовлетворительным. Попробуем улучшить это решение. Проверяем возможность входа в критический интервал, а потом устанавливаем указатель. Cobegin Процесс 1: begin b1:=true; while b2 do ; Критический интервал 1; b1:= false; Оставшаяся часть процесса 1; end; Процесс 2: begin b2:=true; while b1 do ; Критический интервал 2; b2:= false; Оставшаяся часть процесса 2; end; coend; В рассматриваемом случае конкурирующему процессу запрашивается вход в его критический интервал ещё до проверки возможности выполнения критического интервала рассматриваемого процесса. Это гарантирует нам блокировку другого процесса заранее. Однако в приведённой программе оба процесса могут одновременно начать выполняться и, изменив значения переменных b1 и b2, попасть в цикл ожидания. Такая ситуация является тупиковой (так называемый дедлок).
9
В многозадачных системах с разделением процессорного времени использование общих переменных для решения задачи взаимного исключения будет выглядеть иначе. Рассмотрим использование одной переменной FLAG, показывающей нахождение какого-либо процесса в критическом интервале (допустим, FLAG=1- критический интервал свободен, а FLAG=0- занят). Begin mov FLAG,1 Cobegin Process1: begin . . loop: test FLAG,1 jz loop mov FLAG,0 Критический интервал mov FLAG,1 end; Process2: begin . . loop: test FLAG,1 jz loop mov FLAG,0 Критический интервал mov FLAG,1 end; Coend; end; Здесь первые команды mov сбрасывают флажок в 0 пытаясь предотвратить доступ к критическому интервалу другому процессу. Последние команды mov устанавливают флажок в 1, разрешая другому процессу войти в критический интервал. При таком решении имеется возможность входа обоих процессов в критический интервал. Предположим, что FLAG=1 и происходит такая последовательность событий: 1. Выполняется команда test в процессе 1. 2. До выполнения команды jz в процессе 1 происходит переключение на процесс 2. 3. Выполняется команда test в процессе 2. Очевидно, что при таком подходе к решению задачи оба процесса могут находиться в критическом интервале. Лекция 3. Средства взаимного исключения. Использование специальных команд Рассмотрим вопрос использования специальных команд “Проверить и установить” – Test & Set (TS). (Tset – в микропроцессоре Z80 ). Команда TS работает с некоторой глобальной переменной х. TS выбирает некоторый разряд двоичной переменной х и помещает его в признак 10
результата, а затем записывает, если это битовая переменная, во все разряды 1. Таким образом, мы получаем значение переменной х “свободно” или “занято” и запрещаем автоматически дальнейшее её использование установкой 1. Важной особенностью команды TS является её неделимость, которая заключается в том, что её выполнение не может быть прервано (это единая операция). Введём аналог команды TS на языке высокого уровня. Процедура (или функция) TS(x) читает значение булевой переменной х и присваивает ей значение false. Эта процедура является неделимой (существенное предположение для неё). Вариант 1 Чтение х в локальную переменную (проверка можно ли пройти в критический интервал). Var x : boolean; begin x:= true; cobegin Процесс 1: Var b1: boolean; begin b1:= false; while not b1 do b1:=TS(x); Критический интервал 1; x:= true; Оставшаяся часть процесса 1; end; Процесс 2: Var b2: boolean; begin b2:= false; while not b2 do b2:=TS(x); Критический интервал 2; x:= true; Оставшаяся часть процесса 2; end; coend end Каждый процесс начинает работу с того, что присваивает своей локальной переменной (b1 и b2) значение false и тем самым обеспечивает выполнение одного повторения цикла while. В теле цикла с помощью функции TS вычисляется значение глобальной переменной х, которая показывает возможность входа в критический интервал. Поскольку TS является неделимой, то даже при строго одновременном обращении только один из процессов получит возможность входа в критический интервал. Второй процесс при этом будет находиться в цикле, ожидая изменения глобальной переменной х. Это изменение производится после выхода из критического интервала. Такое нахождение процесса в цикле называют циклом активного ожидания, потому что при этом выполняются команды процессора (иначе говоря, занимается процессорное время).
11
Лекция 4. Решение задачи взаимного исключения с помощью семафоров Семафоры - специальные переменные, которые могут разделяться параллельно выполняемыми процессами. Если такая переменная может принимать 2 значения {1 и 0}, то она называется двоичным семафором. С каждой такой переменной связывается очередь, содержащая список процессов, ожидающих открытия семафора. Если переменная равна единице, то семафор открыт, а при равенстве нулю- закрыт. Кроме того, над каждым семафором S определяются специальные операции P(S) и V(S): P(S) – установить (S) V(S) – освободить (S) Будем рассматривать их как функции, реализуемые ядром операционной системы. Выполнение этих операций может быть связано с изменением состояния процесса, обратившегося к супервизору, а так же– с изменением состояния очереди семафора. Для двоичного семафора определим: Установить (S) begin if S=1 then S:=0 {закрыть семафор} else begin Перевести процесс, выполняющий операцию “Установить”, из состояния “активный” в состояние “блокированный”; Включить этот процесс в очередь к семафору S; Выбрать очередной процесс для исполнения; end; end; Освободить (S): begin if очередь семафора S не пуста then Выбрать процесс из очереди и перевести его из состояния “блокированный” в состояние “готов”; else {открыть семафор} S:=1; end. Алгоритм решения задачи взаимного исключения с помощью семафоров. Var S: семафоp; Begin S:=1; cobegin процесс 1:begin Установить (S); Критический интервал 1; Освободить (S); Основная часть процесса 1; end процесс 2:begin Установить (S); Критический интервал 2; Освободить (S); 12
Основная часть процесса 2; end coend; Здесь необходимо отметить следующее: 1) отсутствуют циклы “активного ожидания”; 2) не нужны вспомогательные переменные и операторы. Пример: Пусть имеется 5 процессов: P1, P2, P3, P4, P5, которые работают с одним семафором S. операция
Критический тервал
ин- Очередь (S)
Шаг
процесс
0 1 2 3 4 5 6 7 8 9 10
------- ------------- ------------------- ------------КИ1 Nil (пуста) Р1 Установить Nil (пуста) Р1 Освободить – КИ2 Nil (пуста) Р2 Установить КИ2 Р3 Р3 Установить КИ2 Р3, Р4 Р4 Установить КИ3 Р4 Р2 Освободить КИ3 Р4, Р5 Р5 Установить Р5 КИ4 Р3 Освободить Освободить КИ5 Nil (пуста) Р4 Nil (пуста) Р5 Освободить –
S
1 0 1 0 0 0 0 0 0 0 1
Лекция 5. Другие определения семафоров Если переменная (S), называемая семафором, может принимать целое значение, отличное от {0,1}, то семафор называется числовым. Числовые семафоры могут быть 2 видов: а) числовые семафоры, принимающие только положительные значения; б) числовые семафоры, принимающие отрицательные значения. В зависимости от типа семафора основные операции определяются по-разному. Для а): Установить (S): if S>0 then S:=S-1 else begin Перевести процесс, выполняющий “Установить”, в “блокированное” состояние; Включить этот процесс в очередь S; Запустить очередной “готовый” процесс; end. Освободить (S); if очередь семафора не пуста then Выбрать процесс из очереди S и перевести его из состояния “блокирован” в состояние “готов” else S:=S+1; Числовые семафоры с положительными значениями могут быть использованы в тех случаях, когда требуется пропустить заданное число процессов, а затем закрыть семафор.
13
Для б): Установить (S): S:=S-1; if S