Joyce — безопасный язык программирования для параллельных вычислений, разработанный Пером Бринчем Хансеном в 1980-х годах. [1] Он основан на последовательном языке Pascal и принципах взаимодействия последовательных процессов (CSP). Он был создан для устранения недостатков CSP, чтобы применяться в качестве языка программирования, а также для предоставления инструмента, в основном для обучения, для реализации распределенных вычислительных систем.
Язык основан на концепции агентов ; параллельно выполняемых процессов, которые взаимодействуют только с помощью каналов и передачи сообщений . Агенты могут активировать субагентов динамически и рекурсивно . Разработка Joyce легла в основу языка SuperPascal , также разработанного Хансеном около 1993 года.
Joyce основан на небольшом подмножестве Pascal, расширенном функциями, вдохновленными CSP для параллелизма. [2] В следующих разделах описываются некоторые из наиболее новых функций, которые были введены.
Агент — это процедура, состоящая из набора операторов и, возможно, вложенных определений других агентов. Агент может динамически активировать субагентов, которые выполняются одновременно с их создателем . Агент может завершить работу только тогда, когда все его субагенты также завершились. Например, агент process2
активирует process1
:
агент процесс1 ( x , y : целое число ) ; начинать ...конец ;агент процесс2 () ; использовать процесс1 ; начинать процесс1 ( 9 , 17 ) ; конец ;
Активация агента создает новые экземпляры всех локальных переменных , а значение каждого формального параметра копируется в локальную переменную. Таким образом, агенты не могут получить доступ к переменным других агентов и могут общаться только через каналы. Это ограничение предотвращает проблемы, связанные с использованием общих переменных, такие как состояния гонки .
Агенты общаются через сущности, называемые каналами . Каналы имеют алфавит, определяющий набор символов, которые могут передаваться. Каналы создаются динамически и доступны с помощью переменных порта . Тип порта определяется отдельным набором символов, составляющих его алфавит. Символы с несколькими значениями определяются с помощью определенного типа. Например:
поток = [ целое число ( целое число ) , eos ] ;
Символ int(integer)
обозначает символ сообщения , называемый int
любым целым значением. Второе объявление символа без типа eos
(конец потока) называется сигналом . После определения типа порта можно объявить переменную порта этого типа:
из: потокв : поток
И затем сущность канала, внутренняя по отношению к создавшему ее агенту, может быть активирована следующим образом:
+вне;
Символы затем могут быть отправлены и получены по каналам с использованием операторов ввода и вывода в стиле CSP ?
и !
соответственно. Связь может произойти только в том случае, если есть принимающий агент, соответствующий отправляющему агенту. Принимающий агент должен ожидать получения отправляемого типа символа. Например, значение 9, за которым следует eos
символ, отправляется по порту out
:
вон ! инт ( 9 ) вон ! еос
И целочисленное сообщение принимается в переменную соответствующего типа, за которой следует eos
:
получено : целое число в ? int ( получено ) в ? eos
Опросные утверждения основаны на концепции защищенных альтернатив CSP. Опросное утверждение состоит из набора утверждений, каждое из которых защищено утверждением входного канала. Когда связь между передающим агентом и охраной совпадает, выполняется охрана, за которой следует соответствующее утверждение. Например:
голосование в ? X -> x := x + 1 | в ? Y -> y := y + 1конец
Если порт in
отслеживается на наличие сигналов X
или Y
, при соответствующей связи соответствующие переменные x
или y
увеличиваются.
Joyce был разработан как безопасный язык в том смысле, что компилятор сможет обнаружить все нарушения правил языка.
Ниже приведен полный пример программы, взятый из оригинальной статьи, представляющей язык программирования Джойса [1], реализующий алгоритм генерации простых чисел на основе метода просеивания для генерации простых чисел . sieve
Агенту отправляется поток целых чисел от его предшественника, причем первое является простым числом. Он удаляет все кратные этому простому числу из потока и активирует последующее. Это продолжается до тех пор, пока сигнал eos
не распространится по набору сит.
агент sieve ( inp , out : stream ) ; var more : boolean ; x , y : integer ; succ : stream ; begin poll inp ? int ( x ) -> + succ ; sieve ( succ , out ) ; more := true | inp ? eos -> out ! eos ; more := false end ; while more do poll inp ? int ( y ) -> if y mod x <> 0 then succ ! int ( y ) | inp ? eos -> out ! int ( x ) ; succ ! eos ; more := false end ; end ;
Следующий агент инициализирует набор агентов решета и вводит в них поток целых чисел от 3 до 9999.
агент простые числа ; использовать генерацию , просеивание , печать ; var a , b : поток ; начало + a ; + b ; генерировать ( a , 3 , 2 , 4999 ) ; просеивание ( a , b ) ; печать ( b ) конец ;
Из-за параллельного выполнения процедур агента традиционная схема последовательного распределения стека не может быть использована, поскольку записи активации вызовов агента не следуют шаблону «последний пришел — первый вышел». Вместо этого отношения создатель-субагент образуют древовидный стек. Для реализации этого поведения используется простая схема, которая работает путем выделения новых записей активации наверху стека и связывания записей активации субагентов с записью их создателя. Эти записи освобождаются только тогда, когда агент завершает работу и они находятся наверху стека. [3] Эффективность этой схемы зависит от структуры и поведения программы, что в некоторых случаях приводит к плохому использованию памяти. Более эффективная схема была реализована в языке Хансена SuperPascal .