Параллельные процессы

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

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

Достоинства описанной парадигмы асинхронного программирования на Акторном Прологе заключаются в том что:

  1. Параллельное программирование значительно упрощается. Здесь можно провести аналогию с историей развития графических пользовательских интерфейсов. В своё время появление событийного программирования помогло радикально упростить создание графических интерфейсов и поднять эту область программирования на качественно иной уровень. Логические акторы являются средством ещё более высокого уровня, чем событийное программирование.
  2. Появляются новые области, в которых можно математически корректно использовать логическое программирование. Например, логическая семантика программы на Акторном Прологе не зависит от того, будет ли она исполнена на отдельном компьютере, где можно гарантировать конечное время ожидания процессов, или в распределённой среде Интернет, где информация может вообще никогда не дойти из одного узла сети в другой.

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

  1. Отсутствие синхронизации процессов и повторные доказательства акторов приводят к увеличению общего объёма вычислений и нагрузки на процессор (на процессоры).
  2. Использование асинхронных процессов требует определённого изменения мышления программиста. Здесь опять же уместна аналогия с развитием графических пользовательских интерфейсов. Потребовалось определённое время, чтобы люди признали, что непредсказуемость получения сообщений в событийном программировании является благом, а не проблемой, и что необходимо переключить внимание с операций над экранными объектами на сами объекты.
  3. Модель вычислений, основанная на асинхронных процессах и повторных доказательствах, в общем случае, не стыкуется со средствами программирования низкого уровня. В своё время для многих программистов оказалось неприятным сюрпризом отсутствие свободного доступа к портам компьютера из программы, работающей в графической оболочке. Точно так же, с идеологией Акторного Пролога плохо стыкуются такие средства как потоковый ввод-вывод и модальные диалоги. Тем не менее, в предопределённых классах языка реализованы текстовые окна, файлы, стандартные модальные диалоги графической оболочки и другие средства низкого уровня. Все эти средства могут быть использованы при написании программ, однако при этом программист должен понимать, что они нарушают логическую семантику языка. То есть при использовании средств низкого уровня программист должен взять на себя часть ответственности за корректность результатов работы программы. Например, если программа должна вывести список чисел в текстовое окно, не забудьте вначале очистить окно. Возможно, что доказательство логического актора, печатающего текст, будет осуществлено повторно. Следовательно, нельзя быть уверенным, что в окне ничего не было напечатано ранее. Заметим, что в идеологии Акторного Пролога средством ввода-вывода наиболее низкого уровня, который ещё соответствует логической семантике языка, оказываются немодальные диалоги.

Процессы и сообщения в Акторном Прологе обозначаются очень просто. Начнём с обозначения процессов.

1. Обозначение процессов

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

Рассмотрим пример P1.A, расположенный в каталоге Concur.

Пример 1. Определение процесса.

-------------------------------------------
-- An example of Actor Prolog program.   --
-- (c) 2002, Alexei A. Morozov, IRE RAS. --
-- A definition of one process.          --
-------------------------------------------
project: (('MyClass'))
-------------------------------------------
class 'MyClass':
--
con     = ('Report');
--
[
Message:-
        con ? show,
        con ? writeln(
                "I have received "
                "a message:"),
        con ? set_color('Blue'),
        con ? writeln(Message),
        con ? set_color('Black').
]
-------------------------------------------

В принципе, все элементы этой программы мы уже рассмотрели в предыдущих разделах. Целевым утверждением программы объявляется процесс, экземпляр класса 'MyClass'. У класса 'MyClass' есть один слот con, значением которого является экземпляр предопределённого класса 'Report', реализующего управление текстовыми окнами. Предложение класса написано с использованием метасредства языка, которое мы ещё не рассматривали. Смысл его состоит в том, что любой предикат, вызванный в мире 'MyClass', может быть унифицирован с переменной Message в заголовке предложения. Следовательно, при получении любого неизвестного заранее сообщения, предложение будет вызвано; оно напечатает полученное сообщение в текстовом окне синим цветом. Например, во время создания процесса в окне будет напечатан предикат goal, который автоматически вызывается при создании экземпляров классов.



Рис. 1.1. Печать сообщений в текстовом окне.

Этот процесс мы будем использовать в качестве элементарного кирпичика при объяснении механизмов передачи информации между процессами в Акторном Прологе.

Различаются несколько состояний процессов, в зависимости от состояния принадлежащих им акторов и некоторых других факторов, которые мы рассмотрим позже. Двумя основными состояниями процесса являются:

  1. Состояние "доказанный", которое означает, что все акторы процесса успешно доказаны и между ними нет логических противоречий.
  2. Состояние "неудачный", которое означает, что доказательство некоторых акторов процесса закончилось неудачей или вообще ещё ни разу не было проведено.

Состояние процесса изменяется в результате обработки посылаемых ему сообщений.

2. Потоковые сообщения

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

Рассмотрим пример Flow1.A, расположенный в каталоге Concur.

Пример 2. Передача потокового сообщения.

-------------------------------------------
-- An example of Actor Prolog program.   --
-- (c) 2002, Alexei A. Morozov, IRE RAS. --
-- Transmission of flow message.         --
-------------------------------------------
project: (('Flow1'))
-------------------------------------------

Экземпляр класса 'Flow1' содержит два процесса - p1 и p2. Первый из них - экземпляр класса 'Receiver', а второй - экземпляр класса 'Sender'. Процессы связаны общей переменной V, через которую будет передано потоковое сообщение. Обратите внимание на использование ключевого слова protecting в определении процесса, посылающего сообщение. Это ключевое слово сообщает транслятору, что значение порта output посылающего процесса должно быть защищено от изменения со стороны других процессов, порты которых не объявлены "защищающими". Таким образом, данные будут передаваться строго в одном направлении, из процесса p2 в процесс p1.

class 'Flow1':
--
p1      = (('Receiver',
                input=V));
p2      = (('Sender',
                protecting: output=V));
--
[
goal.
]
-------------------------------------------

Экземпляр класса 'Receiver' устроен также очень просто. Актор, соответствующий предикату goal этого класса, печатает в текстовом окне значение слота input. Следовательно, любое изменение значения этого слота вызывает повторное доказательство актора и вывод нового сообщения в текстовом окне.

class 'Receiver':
--
input;
--
con     = ('Report');
--
[
goal:-
        con ? write("Common variable= "),
        con ? set_color('Blue'),
        con ? writeln(input),
        con ? set_color('Black').
]
-------------------------------------------

Экземпляр класса 'Sender' выполняет одно-единственное действие. Предикат goal, автоматически вызываемый во время создания этого экземпляра класса, присваивает слоту output строку текста "Message".

class 'Sender':
--
output;
--
[
goal:-
        output== "Message".
]
-------------------------------------------

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



Рис. 2.1. Графическое изображение потоковых сообщений.

Теперь запустим программу и посмотрим, что она напечатает на экране.



Рис. 2.2. Передача потокового сообщения.

Обратите внимание, что процесс p1 начал работать, не дожидаясь данных из процесса p2. Поэтому вначале он напечатал значение #. Константа # используется в Акторном Прологе в качестве начального значения общих переменных процессов, а также для других вспомогательных целей. После изменения общей переменной V процессом p2, процесс p1 осуществил повторное доказательство своего актора, который напечатал новое значение общей переменной.

Завершая рассмотрение потоковых сообщений, заметим, что эта разновидность сообщений по своей природе обладает следующими свойствами:

  1. Потоковые сообщения являются естественным механизмом передачи информации "от одного процесса ко многим" и даже "от многих процессов ко многим". Общая переменная может связывать любое число процессов, и потоковое сообщение будет передано всем процессам, связанным с этой переменной.
  2. Передача информации через общую переменную отменяет предыдущее значение этой переменной. Таким образом, потоковое сообщение реализует отмену устаревшего (переданного ранее) потокового сообщения, если получающий процесс ещё не успел его обработать. Общие переменные являются каналом передачи информации без буферизации.

Для реализации передачи информации с буферизацией в Акторном Прологе используется второй тип сообщений, а именно, прямые сообщения.

3. Прямые сообщения

Прямые сообщения являются асинхронным аналогом обычного вызова методов в классической объектно-ориентированной модели вычислений. Прямые сообщения это просто вызов предложений одного процесса из другого. В отличие от потоковых сообщений, прямые сообщения:

  1. Передаются от "одного процесса к одному".
  2. Всегда доходят до получающего процесса. Прямые сообщения являются методом передачи информации с буферизацией.

В зависимости от правил обработки полученного сообщения различаются две разновидности прямых сообщений - "переключающие прямые сообщения" и "информационные прямые сообщения". Они отличаются тем, что:

  1. Переключающие прямые сообщения переводят принимающий процесс в состояние "неудачный", если вызванное ими повторное доказательство акторов процесса завершилось неудачей.
  2. Информационные прямые сообщения никогда не переводят процесс в состояние "неудачный". Если повторное доказательство акторов завершилось неудачей, информационное прямое сообщение просто отменяется, а процесс остаётся в своём прежнем состоянии.

Вторым важным отличием переключающих прямых сообщений и информационных прямых сообщений является то, что:

  1. Обработка переключающих прямых сообщений производится независимо от того, в каком состоянии, "доказанный" или "неудачный", находится получающий процесс.
  2. Обработка информационных прямых сообщений откладывается, если получающий процесс находится в состоянии "неудачный".

Рассмотрим пример передачи прямых сообщений между процессами Direct1.A в каталоге Concur.

Пример 3. Передача прямых сообщений.

-------------------------------------------
-- An example of Actor Prolog program.   --
-- (c) 2002, Alexei A. Morozov, IRE RAS. --
-- Transmission of direct message.       --
-------------------------------------------
project: (('Direct1'))
-------------------------------------------

Экземпляр класса 'Direct1' содержит два процесса - p1 и p2. Как и в предыдущем примере, первый из них - экземпляр класса 'Receiver', а второй - экземпляр класса 'Sender'. Мир p1 передаётся в качестве параметра конструктору мира p2, чтобы процесс p2 послал ему прямые сообщения.

class 'Direct1':
--
p1      = (('Receiver'));
p2      = (('Sender',
                target=p1));
--
[
goal.
]
-------------------------------------------

Класс 'Receiver' принимает любые прямые сообщения и печатает их в текстовом окне.

class 'Receiver':
--
con     = ('Report');
--
[
Message:-
        con ? show,
        con ? writeln(
                "I have received "
                "a message:"),
        con ? set_color('Blue'),
        con ? writeln(Message),
        con ? set_color('Black').
]
-------------------------------------------

Класс 'Sender' посылает два прямых сообщения. Для посылки переключающих прямых сообщений в Акторном Прологе используется инфикс "<-", а для посылки информационных прямых сообщений - инфикс "<<".

class 'Sender':
--
target;
--
[
goal:-
        target << p(1,2,3),
        target <- q(7,8,9).
]
-------------------------------------------

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



Рис. 3.1. Графическое изображение прямых сообщений.

Вот что программа напечатала на экране:



Рис. 3.2. Передача прямых сообщений.

Обратите внимание, что переключающее сообщение было обработано принимающим процессом раньше, чем прямое. В Акторном Прологе переключающие прямые сообщения всегда имеют больший приоритет, чем информационные прямые сообщения. Заметим также, что потоковые сообщения имеют больший приоритет, чем прямые.

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

Оглавление