Лекция 12:

Параллельность, распределенность, клиент-сервер и Интернет

Ключевые концепции

  • Параллельность и распределенность играют все возрастающую роль в большинстве областей приложения компьютеров.
  • Существует много вариантов параллельности, включая многопроцессорность и мультипрограммность. Еще больше возможностей принесли Интернет, Web и брокеры запросов объектов.
  • Для достижения наибольшей выгоды разработчики параллельных и распределенных приложений могут использовать фундаментальные схемы ОО-технологии: классы, инкапсуляцию, множественное наследование, отложенные классы, утверждения и т. п.
  • Не требуется различать активные и пассивные объекты. По своей природе объекты способны выполнять много операций, если делать их активными, то придется ограничиться только одной операцией.
  • Все главные области приложений параллельности покрываются расширением последовательных ОО-обозначений одним новым ключевым словом ( separate ).
  • Каждый объект обрабатывается некоторым процессором. Процессоры - это абстрактное понятие, описывающее потоки управления; система может использовать столько процессоров, сколько ей необходимо, независимо от числа доступных физических вычислительных устройств (ЦПУ). Должна быть предоставлена возможность определять отображение процессоров на ЦПУ вне текстов программ.
  • Объект, обрабатываемый отдельным процессором, называют сепаратным.
  • Вызовы сепаратных целей имеют разную семантику, скорее, асинхронную, чем синхронную. Поэтому сущности, представляющие сепаратные объекты, должны объявляться таковыми с помощью ключевого слова separate.
  • Правила совместности, из которых, в частности, следует, что сепаратная сущность не может быть присвоена несепаратной, гарантируют отсутствие "предателей" - никакая несепаратная сущность не присоединится к сепаратному объекту.
  • Для получения исключительного доступа к сепаратному объекту достаточно использовать соответствующую ссылку в качестве аргумента сепаратного вызова (вызова с сепаратной целью).
  • Цель сепаратного вызова в свою очередь должна быть сепаратным формальным аргументом объемлющей подпрограммы.
  • Предусловия сепаратных целей не могут сохранить свою обычную семантику условий корректности (это называется "парадоксом параллельных предусловий"). Они служат условиями ожидания.
  • Разработанный в этой лекции механизм охватывает многозадачность, разделение времени, многопоточность, вычисления в архитектуре клиент-сервер, распределенную обработку в сетях (в частности, в Интернет), сопрограммы и приложения реального времени.

Библиографические замечания

Описанный в этой лекции подход к параллельности вырос из доклада на конференции TOOLS EUROPE [M 1990a], а затем был пересмотрен в работе [M 1993b], из которой почерпнута некоторая часть материала данной лекции (в частности, примеры). Сейчас он известен под аббревиатурой SCOOP ("Simple Concurrent Object-Oriented Programming" - Простое Параллельное ОО-Программирование). Джон Потер (John Potter) и Гинва Жалул (Ghinwa Jalloul) разработали вариант, который включает явную инструкцию hold [Jalloul 1991], [Jalloul, 1994]. Ожидание по необходимости было введено Дэнисом Каромелем (Denis Caromel) [Caromel 1989], [Caromel1993].

Первая реализация описанной здесь модели была выполнена Терри Тангом (Terry Tang) и Ксавьером ле Вуршем (Xavier Le Vourch). Оба внесли свой вклад.

Хорошим учебником по традиционным подходам к параллельности является [Ben Ari 1990]. Ссылки на оригинальные работы: по семафорам [Dijkstra 1968a] (там же появилась задача об "обедающих философах"), по мониторам [Hoare 1974], по путевым выражениям [Campbell 1974]. Первоначально модель CSP была описана в [Hoare 1978]; в книге [Hoare 1985] эта модель уточнена с упором на ее математические свойства. Occam2 описан в [Inmos 1988]. Архив по CSP и Occam доступен в Оксфордском университете: http://www.comlab.ox.ac.uk/archive/csp.html (Я благодарен Биллу Роскоу (Bill Roscoe) из Оксфорда за помощь в понимании деталей CSP.) Другой важной математически обоснованной моделью являются CCS (Communicating Concurrent Systems - Взаимодействующие Параллельные Системы) [Milner 1989]. Мимоходом упомянутые в этой лекции метод и инструментальное средство Linda Карьеро (Carriero) и Гелернтера (Gelernter) [Carriero 1990] должны знать все, кто интересуется параллелизмом.

В специальном выпуске журнала Communications of the ACM [M 1993a] представлены многие подходы к параллельному ОО-программированию, первоначально описанные в статьях по параллелизму на различных конференциях TOOLS.

Примерно в то же время появился и другой сборник статей [Agha 1993]. Более ранняя коллективная книга [Yonezawa 1987] под редакцией Йонезава (Yonezawa) и Токоро (Tokoro) послужила катализатором для многих работ в этой области и до сих пор является хорошим источником. Среди других обзоров отметим диссертацию [Papathomas 1992] и статью [Wyatt 1992]. Еще один сборник ряда авторов [Wilson 1996] охватывает параллельные расширения C++.

Модель акторов Хьюита (Hewitt) и Агха (Agha) повлияла на многие подходы к ОО-параллельности; она описана в статье [Agha 1990] и в книге [Agha1986]. Акторы - это вычислительные агенты, похожие на активные объекты, каждый со своим собственным почтовым адресом и поведением. Они взаимодействуют друг с другом с помощью сообщений, посылаемых по их адресам; для достижения асинхронного поведения эти сообщения буферизируются. Актор обрабатывает сообщения с помощью функций, после того как некоторое сообщение обработано, прежнее поведение актора изменяется на "замещающее поведение".

Одним из самых ранних и наиболее исследованных параллельных ОО-языков является POOL [Amerika 1989]; в POOL используется понятие активного объекта, которое в сочетании с наследованием приводит к известным проблемам. Поэтому наследование было введено в этот язык только после тщательного изучения, приведшего к разделению механизмов наследования и выделения подтипов. Проект POOL примечателен также тем, что с самого начала продемонстрировал заинтересованность в формальной спецификации языка.

Много важной работы по параллельным ОО-языкам было проведено в Японии. Уже цитированная книга [Yonezawa 1987] содержит описание нескольких важных японских достижений, таких как ABCL/1 [Yonezawa 1987a]. ОО-операционная система MUSE, разработанная в лаборатории информатики корпорации Сони (Sony), была представлена Токоро (Tokoro) и его коллегами на конференции TOOLS EUROPE 1989 [Yokote 1989]. Термин "аномалия наследования" был введен Мацуокой (Matsuoka) и Йонезавой (Yonezawa) [Matsuoka 1993], а в последующих статьях Мацуоки и его сотрудников были предложены разные средства ее разрешения.

Работы по распределенным системам особенно активно велись во Франции, где были созданы операционная система CHORUS с описанным в [Lea 1993] ОО-расширением, язык GUIDE и система Краковяка (Krakowiak) и др. [Balter 1991], система Шапиро (Shapiro) [Shapiro 1989]. В области программирования массивных параллельных архитектур, ориентированных на научные приложения, Жан-Марк Жезекель (Jean-Marc Jezequel) разработал систему EPEE [Jezequel 1992], [Jezequel 1996], [Guidec 1996].

Важной также оказалась работа Nierstrasz и его коллег из университета Женевы по языку Hybrid [Nierstrasz1992], [Papathomas 1992], в котором нет двух категорий объектов (активных и пассивных), но вместо этого используется понятие потока управления, называемого активностью (activity). Основным механизмом взаимодействия является удаленный вызов процедур, синхронный или асинхронный.

Среди других важных проектов отметим DRAGOON [Atkinson 1991], в котором, как и в механизме данной лекции, для выражения синхронизации используются пред- и постусловия, и pSather [Feldman 1993], базирующийся на понятии потока и предопределенном классе MONITOR.

К этому списку следовало бы добавить еще много других работ. Более подробные обзоры были процитированы в начале этого раздела. В трудах семинаров, регулярно проходящих при конференциях ECOOP и OOPSLA, таких как [Agha1988], [Agha1991], [Tokoro 1992], описано многообразие исследовательских проектов, и они весьма ценны для тех, кто хочет узнать, какие проблемы исследователи считают наиболее безотлагательными.

На разных стадиях для предложенной в этой лекции работы были полезны комментарии и критические замечания многих людей. В дополнение к уже процитированным в первых двух параграфах этого раздела коллегам перечислим Мордехая Бен-Ари (Mordechai Ben-Ari), Ричарда Бьеляка (Richard Bielak), Джона Бруно (John Bruno), Поля Дюбуа (Paul Dubois), Карло Гецци (Carlo Ghezzi), Петера Лёра (Peter Lohr), Дино Мандриоли (Dino Mandrioli), Жан-Марка Нерсона (Jean-Marc Nerson), Роберта Светцера ( Robert Switzer) и Кима Вальдена (Kim Walden).

Упражнения

У12.1 Принтеры

Завершите определение класса PRINTER из третьего раздела этой лекции, реализующего очередь с помощью ограниченного буфера. Обратите внимание на то, что подпрограммы для работы с очередью, такие как print, не нуждаются в обработке специального "запроса на остановку" задания печати (у print в качестве предусловия может быть not j.is_stop_request ).

У12.2 Почему импорт должен быть глубоким

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

У12.3 "Аномалия наследования"

Предположим, что в примере BUFFER, использованном для иллюстрации "аномалии наследования" каждая подпрограмма специфицирует свое выходное состояние с помощью инструкции yield, например, как в:

put (x: G) is
    do
        "Добавляет x к структуре данных, представляющей буфер"
        if "Все места сейчас заняты" then
            yield full
        else
            yield partial
        end
    end

Напишите соответствующую схему для remove. Затем определите класс NEW_BUFFER с дополнительной процедурой remove_two (удалить_два) и покажите, что в этом классе должны быть переопределены оба наследуемых компонента (одновременно определите, какие компоненты применимы в каких состояниях).

У12.4 Устранение тупиков (проблема для исследования)

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

У12.5 Приоритеты

Исследуйте, как добавить схему приоритетов к механизму дуэлей класса CONCURRENCY, сохранив совместимость вверх с семантикой, определенной для процедур yield, insist и других процедур, связанных с ними.

У12.6 Файлы и парадокс предусловия

Рассмотрите следующий простой фрагмент некоторой подпрограммы для работы с файлом:

f:  FILE
...
if f /= Void and then f.readable then
    f.some_input_routine
        -- some_input_routine - программа, которая читает
        -- данные из файла; ее предусловием является readable
end

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

У12.7 Замки (Locking)

Перепишите класс LOCKING_PROCESS как наследника класса PROCESS.

У12.8 Бинарные семафоры

Напишите один или несколько классов, реализующих понятие бинарного семафора. ( Указание: начните с классов, реализующих замки.) Как было предложено в конце обсуждения замков, разработайте классы поведения высокого уровня, используемые с помощью наследования и предоставляющие корректные образцы для операций reserve и free.

У12.9 Целочисленные семафоры

Напишите один или несколько классов, реализующих понятие целочисленного семафора.

У12.10 Контроллер сопрограмм

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

У12.11 Примеры сопрограмм

В представлении языка Simula имеется несколько примеров сопрограмм. Примените классы сопрограмм из данной лекции для реализации этих примеров.

У12.12 Лифты

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

У12.13 Сторожа и принцип визитной карточки

Покажите, что процедура set класса WATCHDOG нарушает принцип визитной карточки. Объясните, почему в этом случае все в порядке.

У12.14 Однократные подпрограммы и параллельность

Какова подходящая семантика для однократных подпрограмм в параллельном контексте (как программ, исполняемых один раз при каждом запуске системы, или как программ, исполняемых не более одного раза каждым процессором)?

Алексей Щербанов
Алексей Щербанов
Россия, г. Оренбург
Ксения Маковецкая
Ксения Маковецкая
Россия, Москва, МГЮА им. О.Е. Кутафина, 2014