В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 66
Текст из файла (страница 66)
а затем заказать рандеву с i-ым элементом этого массива М
M(i). поставить (Z);
Применимы к задачным типам и обычные средства образования динамических
структур - ссылочные типы с генератором new. Например
type P is access буф;
P1 : P;
. . .
P1 := new буф; -- создание и запуск нового
-- процесса-буфера
P1.поставить(Z); и т.п.
Итак, имеются средства объявления коллектива исполнителей - асинхронных
процессов. Их можно объявлять "поштучно" - объявлениями задачи или объектов
задачного типа, массово статически - объявлениями массивов и, наконец,
массово динамически - посредством ссылочных задачных типов.
Коммутация процессов. Однако с коммутацией процессов-исполнителей
возникают проблемы, в основном связанные с отсутствием в Аде понятия канала.
Коротко говоря, в Аде отсутствуют естественные средства статического
связывания коллектива процессов (аналогичные репликаторам и массивам каналов
в Оккаме).
Действительно, входы и вызовы входов фиксированы при объявлении
задачного типа. Чтобы связать два объекта этого типа, нужно передать им (или
хотя бы одному из них) атрибуты партнера. Однако при объявлениии типов еще
нет нужных объектов, а при объявлении объектов (и массивов процессов)
передать нужные атрибуты нельзя (инициализация ограниченных приватных
запрещена, да и в общем случае средства инициализации в Аде слабы для
установления нужных связей - ведь каждый элемент массива должен получить
"имя" партнера по рандеву).
Сравните, в Оккаме члены коллектива связывались каналами именно при
описании структуры коллектива (посредством репликаторов).
Поэтому в Аде структуру коллектива процессов приходится создавать
динамически (привлекая аппарат динамических параметров и, возможно, ссылок).
Теперь все готово для попытки воплотить в Аде параллельное
преобразование координат.
17.9.2. Параллельное преобразование координат в Аде
Вернемся к примеру, рассмотренному ранее для Оккама в п.17.4. Предстоит
решить несколько проблем. Покажем их на примере процессов pa.
Статическое объявление процессов
Во-первых, придется описывать коллектив процессов этого вида.
Сформируем его статически, а связывать процессы будем динамически (так как
статических средств для этого нет, см. выше). Ясно, что придется ввести
массив процессов. Стало быть, нужно описать тип элементов такого массива.
Ясно, что это должен быть задачный тип Ады. Итак, нужно объявить задачный
тип, например, тип pa, и массив объектов этого типа.
Во-вторых, нужно понять, какие входы нужны объектам типа pa. Прототип в
Оккаме имел четыре канала и это соответствовало симметричному рандеву. Так
как в Аде рандеву ассиметричное, нужно решить, по каким направлениям процесс
будет играть роль мастера, а по каким - клиента, и ввести нужные входы.
Примем, что процесс pa предоставляет услуги партнерам сверху (для принятия
xj) и партнерам справа (для принятия заготовки yi). При этом он сам
пользуется услугами партнера снизу (для передачи ему xj) и партнера слева
(для передачи ему модифицированного yi).
Итак, нужны два входа, которые назовем "сверху" и "справа". Запишем
только что разработанный вариант фрагмента программы
task type pa is
entry сверху (X : in real); -- принять xj сверху
entry справа (X : in real); -- принять yi справа
end pa;
В теле pa должны быть и операторы приема (этих двух входов), и вызовы
входов (партнеров снизу и слева). Так что тело pa имеет вид
task body pa is
xj, yi, aij_xj : real;
. . .
accept сверху (X : in real) do -- верх ? xj
xj := x -- прием от партнера сверху
end сверху;
. . .
accept справа (Y : in real); do -- право ? yi
yi := x -- прием от партнера справа
end справа;
. . .
партнер_снизу. сверху (xj); -- низ ? xj
. . . -- передача партнеру вниз
партнер_слева. справа (yi); -- лево ? yi
. . . -- передача партнеру влево
end pa;
[Если сопоставить с текстом pa на Оккаме, сразу видно неудобство записи
на Аде "малого параллелизма". Не удается выразить параллелизм внутри
процесса pa без новых объявлений задач (со своими входами, параметрами,
вызоами входов). Длинно, неудобно, ненаглядно - Ада для такого параллелизма
не приспособлена!]
В-третьих, самый существенный вопрос. Как сообщить процессу типа pa
конкретные имена его партнеров (т.е. процессов, с которыми следует связать
имена партнер-снизу и партнер-слева)? Да и значения aij ему также требуется
передать. Выше объяснено, почему это приходится делать динамически (в
частности, при объявлении типа pa процессов-партнеров еще просто нет).
Следовательно, при объявлении типа pa необходимо позаботиться о настройке
процессов (на связь с конкретными партнерами) в период исполнения программы.
Мы пока этого не сделали!.
[При создании массива из элементов типа pa можно было бы согласованно
инициализировать его элементы, присвоив им подходящие значения. Но для этого
нужно иметь возможность "вычислить" нужный процесс и "присвоить" его
компоненте массива. Ада не дает возможности "вычислять" процессы нужным
образом, да и присваивания ограниченным приватным объектам запрещены; а тем
самым и инициализация тоже].
Итак, связать партнеров статически в Аде невозможно (если не все
партнеры известны в момент создания программы).
Подчеркнем, что наши проблемы с коммутацией процессов вызваны в
значительной степени тем, что средства управления рандеву в Аде нельзя
отделить от процессов (точнее, от объявления задач). В Оккаме каналы -
самостоятельные объекты (предназначенные для связывания других
самостоятельных объектов - процессов). Именно разделение этих двух дуальных
видов объектов позволяет легко отложить связывание процессов от момента
описания процесса (и каналов) до момента порождения конкретного процесса.
Канал становится естественным параметром процесса, причем параметром периода
компиляции. Нетрудно породить столько каналов, сколько нужно для связывания,
и указать подходящий канал в качестве аргумента процесса. При этом передача
аргумента-канала каждому партнеру выполняется полностью независимо (при
порождении партнера).
Обратите внимание, удачно работает адекватная абстракция-конкретизация
(абстракция от партнеров - канал как абстрактное симметричное рандеву и
конкретизация - сначала настройка на одного партнера, затем на другого).
В Аде именно отсутствие такой абстракции приводит к рассматриваемой
нами сейчас проблеме коммутации. Кстати, появление нужной абстракции вполне
возможно прогнозировать на основе общих критериев качества абстракции-
конкретизации - нужно было лишь учесть потребность в развитой коммутации
процессов.
Организация динамической настройки процессов
Итак, в Аде средств коммутации при порождении процессов нет. Приходится
программировать динамическое связывание партнеров. Но динамическое - значит
при исполнении программы, а единственное средство связи с работающим
процессом в Аде (как и в Оккаме) - рандеву. Причем это обязательно должно
быть рандеву родительского процесса со своими потомками - потомки до
настройки еще не могут "знать" друг друга! (Почему?)
Такое настраивающее рандеву в общем случае должно передать процессу
информацию либо только о его собственных координатах (с тем, чтобы сам
процесс "вычислил" своих партнеров), либо передать процессу информацию
непосредственно о самих партнерах. Хотя первое решение в Аде воплотить
проще, рассмотрим именно второй вариант по двум причинам. Во-первых, мы
сопоставляем возможности Ады и Оккама по части коммутации процессов, а в
исходной программе на Оккаме процесс вида pa не вычислял своих партнеров, а
был настроен на них. Во-вторых, не менее существенно, что нетрудно так
обобщить постановку задачи, чтобы процесс вида pa в принципе не мог
вычислить не только конкретного партнера, но даже его тип.
Вопрос. Как это сделать?
Подсказка. В сущности, от процессов, например, вида py требуется лишь
способность воспринимать информацию, передаваемую процессами вида pa.
Итак, будем считать, что при настройке процессу вида pa необходимо
передать информацию, позволяющую ему обратиться к партнеру в общем случае
заранее неизвестного вида (типа). Мы пришли к необходимости подправить
описание процесса pa - нужен еще один вход для настройки процессов. Его
параметрами должны стать aij, а также партнеры снизу и слева.
Принципиальные решения приняты: массив процессов типа pa (двумерный) и
три входа - два для работы, один для настройки. Однако технические проблемы
остаются.
1. Первая техническая проблема - параметрами входа "настройка" в
объектах типа pa могут оказаться объекты того же типа pa. И тип pa
становится объявляемым непосредственно через себя, что в Аде недопустимо.
Ведь использовать имя типа до его полного объявления можно лишь в так
называемых неполных объявлениях (предъобъявлениях) перед объявлением
ссылочных типов. Приходится вводить тип ссылок на pa
type pa;
type ppa is access pa;
type pa is . . .;
2. Вторая техническая проблема - для настройки необходимо вычислить
ссылки на отдельные компоненты массивов процессов (если, как мы и
договорились, не заставлять процесс вида pa "знать" о том, что одни его
партнеры организованы в массив определенной структуры и доступ к ним
возможен посредством индексов этого массива, другие партнеры - в массив
другой структуры и т.п.). В Аде есть предопределенная атрибутная функция для
вычисления адреса объекта типа Р - P'ADRESS - однако она доставляет не
значение нужного типа (а именно ppa), а значение типа adress из
предопределенного пакета SYSTEM. Из-за этого придется заменить массив
процессов типа pa массивом ссылок типа ppa. Сами процессы типа pa нужно
будет порождать операцией new (т.е. динамически).
Так что отсутствие адекватной абстракции (от партнера) привело через
потребность в динамической настройке к необходимости динамического
порождения процессов - все больше нагрузка на целевую машину, столь
неприятная для Ады.
3. Третья техническая проблема - как учесть "краевые элементы". Ведь
"нижние" и "левые" процессы типа pa должны общаться с процессами совсем
другого типа.
Нам снова не хватает посредников - каналов Оккама. Для них было не
важно, с процессом какого типа связать - лишь бы протоколы были согласованы
(кстати, еще одна полезная абстракция - от типа связываемого процесса). А в
Аде мешает контроль за типами параметров у входа "настройка" (почему?).
Pешение можно построить на основе так называемого вариантного
комбинированного типа, дискриминантом которого стал бы "вид_процесса", а
выбираемыми компонентами - процессы разных типов. При этом в самих этих
процессах придется разбираться, процесс какого типа оказался партнером слева
или снизу, и использовать подходящий селектор записи (единым селектором для
процессов разных типов не обойтись - опять же из-за контроля типов). Тип
процесса (точнее, его признак) придется задавать при настройке (конечно,
вместе с дискриминантом вариантной записи).
Рассмотрим это решение подробнее (концентрируя внимание в основном на
процессах типа pa), хотя в полной мере оно нас удовлетворить заведомо не
может - ведь процесс типа pa должен "знать" типы потенциальных партнеров и
разбираться с этими типами самостоятельно. Заодно познакомимся с вариантными
типами Ады (в первом приближении вполне аналогичными паскалевским).
Решение с вариантным типом
package координаты_1 is