Главная страница
Навигация по странице:

  • Множественная пометка

  • Взаимодействие – обмен сообщениями

  • Ввод и вывод

  • Взаимодействия

  • Разделяемые ресурсы

  • Поочередное использование

  • Общая память

  • ТЕОРИЯ ВЫЧИСЛИТЕЛЬНЫХ СИСТЕМ. Теория вычислительных процессов


    Скачать 2.17 Mb.
    НазваниеТеория вычислительных процессов
    АнкорТЕОРИЯ ВЫЧИСЛИТЕЛЬНЫХ СИСТЕМ.doc
    Дата01.04.2018
    Размер2.17 Mb.
    Формат файлаdoc
    Имя файлаТЕОРИЯ ВЫЧИСЛИТЕЛЬНЫХ СИСТЕМ.doc
    ТипДокументы
    #17485
    страница15 из 20
    1   ...   12   13   14   15   16   17   18   19   20

    Помеченные процессы
    Помечать процессы особенно полезно при создании групп сходных процессов, которые в режиме параллельной работы представляют некоторые идентичные услуги их общему окружению, но никак не взаимодействуют друг с другом. Это означает, что все они должны иметь взаимно непересекающиеся алфавиты. Чтобы этого достичь, снабдим каждый процесс отдельным именем; каждое событие помеченного процесса помечено тем же именем. Помеченное событие l.x, где х – его имя, аl – метка.

    Процесс P с меткой l обозначают где l:P.Он участвует в событии l.x, когда по определению Pучаствует в х.

    Пример 3.18. Два работающих рядом торговых автомата

    (лев: ТАП) || (прав: ТАП).

    Алфавиты этих процессов не пресекаются, и каждое происходящее событие помечено именем того устройства, на котором оно происходит. Если перед их параллельной установкой устройства не получили имен, каждое событие будет требовать участия их обоих, и тогда пара машин будет неотличима от одной. Это является следствием того, что (ТАП || ТАП) = ТАП.

    Пометка позволяет использовать процессы наподобие локальных переменных в языке высокого уровня, описанных в том блоке программы, где они используются.

    Множественная пометка
    Определение пометки можно расширить, позволив помечать каждое событие любой меткойl из некоторого множества L.ЕслиP – процесс, определим (L:P) как процесс, ведущий себя в точности какPс той разницей, что он участвует в событииl.x (где l L, x P ), если по определениюP участвует в х.

    Пример 3.19. Лакей – это младший слуга, который имеет одного хозяина, провожает его к столу и из-за стола и прислуживает ему, пока тот ест:

    ЛАКЕЙ = {садится, встает}

    ЛАКЕЙ = (садится → встает → ЛАКЕЙ)

    Лакей обслуживающего всех пятерых философов по очереди определим:

    L= {0, 1, 2, 3, 4}

    ОБЩИЙ ЛАКЕЙ =(L: ЛАКЕЙ).

    Общего лакея можно нанимать на период отпуска слуги для предохранения обедающих философов от тупиковой ситуации. Конечно, в течение этого времени философам придется поголодать, ибо находиться за столом они смогут только по очереди.
    Взаимодействие – обмен сообщениями
    В предыдущих разделах было введено и проиллюстрировано общее понятие события как действия, не имеющего протяженности во времени, наступление которого может требовать одновременного участия более чем одного независимо описанного процесса. В этом разделе внимание будет сосредоточено на одном специальном классе событий, называемых взаимодействиями. Взаимодействие состоит в передаче сообщений и является событием, описываемым парой c.v,где c– имя канала по которому происходит взаимодействие, а v– значение передаваемого сообщения. Такое обозначение мы уже использовали – это описание процесса КОПИБИТ(см. пример 3.3).

    Различают следующие виды каналов:

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

    Асинхронно/синхронные. Операция передачи сообщения асинхронная - она завершается сразу (сообщение копируется в некоторый буфер, а затем пересылается одновременно с работой процесса-отправителя), не ожидая того, когда данные будут получены приемником. Операция приема сообщения синхронная: она блокирует процесс до момента поступления сообщения.

    Асинхронные. Обе операция асинхронные, то есть они завершаются сразу. Операция передачи сообщения работает, как и в предыдущем случае. Операция приема сообщения, обычно, возвращает некоторые значения, указывающие на то, как завершилась операция - было или нет принято сообщение.

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

    Множество всех сообщений, с помощью которых Р может осуществлять взаимодействие по каналу с, определяется как

    с(Р) = {v|c.v P}.

    Кроме того, определены функции, выбирающие имя канала канал(c.v) = c и значение сообщения сообщ(c.v) =v.

    Каналы используются для передачи сообщений только в одном направлении и только между двумя процессами.
    Ввод и вывод
    Пусть v– элемент с(Р).Процесс, который сначала выводит vпо каналу с,а затем ведет себя как Р,обозначим

    (с !vP) = (c.vP).

    Единственное событие, к которому этот процесс готов в начальном состоянии. – это взаимодействие c.v.

    Процесс, который сначала готов ввести любое значение х, предаваемоепо каналу с,а затем ведет себя как Р(х),обозначим

    (с ?vP(х)) = (у: {y | канал(у) =c}P(сообщ(у))).

    Пусть P,Q – процессы, с – выходной канал Pи входной канал Q. Если P,Q объединены в параллельную систему (P||Q), то взаимодействие по каналу с будет происходить всякий раз, когда P выводит сообщение, а Q в тот же самый момент вводит его. Выводящий процесс однозначно определяет значение передаваемого сообщения, тогда как вводящий процесс готов принять любое поступающее значение. Поэтому в действительности происходящим событием будет взаимодействие c.v,где v– значение. Определяемое выводящим процессом. Отсюда вытекает очевидное ограничение, что алфавиты на обоих концах канала должны совпадать, т.е. с(Р) = с(Q).

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

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

    лев(КОПИР) = прав(КОПИР)

    КОПИР =µХ.(лев ? хправ! хХ).

    Если лев ={0, 1},то КОПИРпочти полностью совпадает с КОПИБИТ.

    Пример 3.21. Процесс, похожий на КОПИР, с той разницей, что каждое вводимое число перед выводом удваивается:

    лев= прав =Nat

    УДВ =µХ.(лев ? хправ! (х + х) →Х).

    Пример 3.22. Процесс, принимающий сообщение по одному из двух каналов лев1 и лев2и немедленно передающий его направо:

    лев1= лев2= прав

    СЛИЯНИЕ =(лев1 ? х → прав! х →СЛИЯНИЕ

    | лев2 ? х → прав! х →СЛИЯНИЕ).
    Взаимодействия
    Пусть P,Q – процессы, с – выходной канал Pи входной канал Q. Тогда множество, состоящее из событий-взаимодействий c.v, содержится в пересечении алфавита P с алфавитом Q.Если процессы объединены в параллельную систему (P||Q), то взаимодействие c.v может происходить только когда в этом событии одновременно участвуют оба процесса, т.е. в тот момент, когда P выводит значение vпо каналу c, а Q одновременно вводит это значение. Вводящий процесс готов принять любое возможное поступающее сообщение, и поэтому то, какое именно значение передается, определяет выводящий процесс.

    Таким образом, вывод можно рассматривать как специальный случай операции префиксации, а ввод – как специальный случай выбора.

    L1.(с !vP) || (с ?xQ(x)) = с !v(P||Q(v)).
    Подчинение
    Пусть P,Q – процессы, такие, что Р Q.В комбинации (P||Q) каждое действие Рможет произойти, только если это позволяет Q,тогда как Qможет независимо осуществлять действия из (Р - Q)без разрешения и даже без ведома партнера Р. Таким образом, Рслужит по отношению к Q подчиненным процессом, тогда как Qвыступает как главный процесс. Это записывается так: Р // Q.Отсюда: // Q) =(Р - Q).

    Обычно бывает удобно давать подчиненному процессу имя, скажем m,которое используется главным процессом для всех взаимодействий с подчиненным. Каждое взаимодействие по каналу с обозначается тройкой m.c.v, где m.c(m:P) =с(Р),а v с(Р).

    В конструкции (m:P//Q) процесс Qвзаимодействует с P по каналам с составными именами вида m.c,тогда как P для этих же взаимодействий использует соответствующий простой канал с. Так как все эти взаимодействия скрыты от обстановки, имя m недоступно снаружи; следовательно, оно служит локальным именем подчиненного процесса.

    Подчинение может быть вложенным, например (n: (m:P//Q)//R).Процесс R не имеет возможности ни непосредственно взаимодействовать сP, ни знать о существовании Pи его имени m.

    Пример 3.23.удв: УДВ // Q.

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

    удв.лев ! е → (удв.прав? х → …).

    Пример 3.22. ст: СТЕК //Q.

    Внутри главного процесса Q ст.лев!vиспользуется для проталкивания в верхушку стека, а ст.прав?x выталкивает верхнее значение. Использование конструкции выбора позволяет рассматривать ситуацию, когда стек пуст:

    (ст.прав ?xQ1(х)| ст.пустQ2).

    Если стек не пуст, то выбирается первая альтернатива; если пуст, то выбор второй альтернативы позволяет избежать тупика.

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

    Пример 3.24.

    ФАКТ =µХ.лев?n → (if n = 0 then (прав!1 →X)

    else (f: X // (f.лев!(n – 1) →f.прав?yправ!(ny) → X)).

    Подпрограмма ФАКТ использует каналы лев и прав для обмена результатами и параметрами с вызывающим ее процессом, а каналы f.лев иf.прав –для взаимодействия со своим подчиненным процессом f.
    Разделяемые ресурсы
    Обозначение (m: //S) мы использовали для именованного подчиненного процесса (m: R), единственной обязанностью которого является удовлетворение потребностей главного процесса S. Предположим теперь, что S состоит из двух параллельных процессов (P||Q) и они обануждаются в услугах одного и того же подчиненного процесса (m: R). К сожалению, PиQне могут взаимодействовать с R по одним и тем же каналам, потому что тогда эти каналы должны содержаться в алфавитах обоих процессов, и, значит, согласно определению оператора ||, взаимодействия с (m: R) могут происходить, только когда PиQодновременно посылают одно и то же сообщение, что далеко не соответствует желаемому результату. Нам же требуется своего рода чередование взаимодействий между Pи (m: R) с взаимодействиями между Qи (m: R). В этом случае (m: R) служит как ресурс, разделяемый PиQ; каждый из них использует его независимо, и их взаимодействия с ним чередуются.

    Когда все процессы-пользователи известны заранее, можно организовать работу так, чтобы каждый процесс-пользователь имел свой набор каналов для взаимодействий с совместно используемым ресурсом. Эта техника применялась в задаче об обедающих философах: каждая вилка совместно использовалась всеми пятью. Общий метод разделения ресурсов дает множественная пометка, которая является эффективным средством создания достаточного числа каналов для независимого взаимодействия с каждым процессом-пользователем. Отдельные взаимодействия по этим каналам произвольно чередуются. Однако при таком методе необходимо знать заранее имена всех процессов-пользователей, и поэтому он не подходит для подчиненного процесса, предназначенного для обслуживания главного процесса, который разбивается на произвольное число параллельных подпроцессов.
    Поочередное использование
    Проблему, вызванную использованием комбинирующего оператора ||, можно избежать, используя параллелизм в форме чередования (P|||Q). Здесь PиQ имеют одинаковые алфавиты, а их взаимодействия с совместно используемыми внешними процессами произвольно чередуются.

    Пример 3.25. Совместно используемая подпрограмма: удв: УДВ // (P|||Q).

    Здесь и P,иQмогут содержать вызов подчиненного процесса

    (удв.лев!v→ удв.прав?х → ПРОПУСК),

    гдеПРОПУСК – процесс, который ничего не делает, но благополучно завершается.

    Пример 3.26. Совместно используемое алфавитно-цифровое печатающее устройство:

    АЦПУ = занят → µХ.(лев?sh!sX | свободен → АЦПУ).

    Здесь h – канал, соединяющий АЦПУ с аппаратной частью устройства. После наступления события занят АЦПУкопирует в аппаратную часть последовательно поступающие по левому каналу строки, пока сигналсвободен не приведет его в исходное состояние, в котором он доступен для использования другими процессами. Этот процесс используется как разделяемый ресурс:

    ацпу: АЦПУ // … (P|||Q)….

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

    ацпу.занят → … ацпу.лев!”Вася” →…ацпу.лев!следстр → … ацпу.свободен.
    Общая память
    Цель этого раздела – привести ряд аргументов против использования общей памяти.

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

    Ячейка общей памяти – это разделяемая переменная:

    (счет: ПЕРЕМ // (счет.лев!0 → (P|||Q))).

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

    Пример 3.27. Взаимное влияние.

    Разделяемая переменная счет используется для подсчета числа исполнений некоторого важного события. При каждом наступлении этого события соответствующий процесс Р или О пытается изменить значение счетчика парой взаимодействий: счет.прав?х;и счет. лев! + 1).

    К сожалению, эти два взаимодействия могут перемежаться аналогичной парой взаимодействий от другого процесса, в результате чего мы получим последовательность

    счет.прав?х → счет.прав?у → счет.лев!(у + 1) → счет.лев!(х+ 1) →....

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

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
















    На рис. 4.1. изображены критические участки процессов Р1, Р2. А, В, С – разделяемые ресурсы.

    Более приемлемое решение было предложено Э. Дейкстрой, которому принадлежит идея использования двоичных семафоров. Семафор - это процесс, поочередно выполняющий действия с именамиР и V:

    СЕМ = (Р → V → СЕМ).

    Он описывается как совместно используемый ресурс (взаискл: СЕМ // ...). При условии, что все процессы подчиняются этой дисциплине, каждый из двух процессов не сможет влиять на изменение счетчика — произвести действие взаискл.V. Таким образом, критический участок, на котором происходит увеличение счетчика, должен иметь вид:

    взаискл. Рсчет.прав?х →счет.лев!(х + 1) → взаискл.V → ….

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

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

    счет:СТ0 //(...Р |||.Q..)

    На самом деле, есть все основания рекомендовать, чтобы каждый совместно используемый ресурс заранее проектировался для своих целей и, чтобы в разработке системы с элементами параллелизма универсальная память не использовалась совместно. Этот метод не только предупреждает серьезную опасность случайного взаимного влияния, но и позволяет получать конструкции, поддающиеся эффективной реализации на сетях распределенных процессорных элементов, а также на одно- и многопроцессорных ЭВМ с физически общей памятью.
    1   ...   12   13   14   15   16   17   18   19   20


    написать администратору сайта