Статьи

Givvy говорит: параллельное программирование не так сложно

Возможно, несколько необычно представить язык программирования на стороне клиента, демонстрируя пример параллельного программирования. Особенно, если вы уже видели несколько приложений GVision GUI в действии во вводной заставке . На самом деле, мы настолько привыкли рассматривать веб-приложение как конечную форму распределенного приложения и, соответственно, всегда и только связывать клиента с уровнем представления, что мы запутываемся или скептически относимся к клиенту как к простому узлу обработки.

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

Тем не менее, я выбрал этот фрагмент Givvy также по более практичным причинам. Во-первых, это довольно распространено и просто, но не бесполезно. Многие языки программирования используют его в качестве примера, так что вы можете легко сравнить различные реализации. Кроме того, это также эффективно для демонстрации моих предыдущих утверждений, в которых я говорил, что реализация модели акторов, предоставляемая Givvy, упрощает подход к параллельному программированию даже разработчиком ОО (но также и программированием в целом. Пример на лету). … любая изменяемая составная структура данных может быть безопасно изменена во время итерации. Нет необходимости использовать специальные объекты итератора).

Конечно, если вы хотите попробовать себя в программе, вам понадобится как среда выполнения ( GVNode ), так и управляющий узел ( cNode ). Напоминаю, что GVision — это распределенная архитектура, поэтому вам обязательно нужно установить как клиентскую, так и серверную часть. Концепция очень похожа на игру с веб-приложением. Там вам нужен веб-сервер / сервер приложений для доставки приложения, а также веб-браузер, чтобы увидеть, как оно ведет себя. Отличительной особенностью GVision является то, что механизм обработки переносится с сервера на клиентскую часть (я предполагаю, что вы уже поняли, что речь не идет о монолитной модели RIA, учитывая динамическую инфраструктуру GVision).

Если вы будете следовать инструкциям на этой странице , вам понравится простота этой инфраструктуры, и через несколько минут вы сможете экспериментировать в распределенном графическом REPL для нескольких соединений со всеми примерами, представленными в дистрибутиве поддержки GVision.(правда, не так много, но каждый заслуживает внимания), наряду со всеми, которые будут опубликованы в будущем, конечно. Вы также узнаете, как создать и запустить распределенную программу Givvy проще и быстрее, чем создавать и отображать HTML-страницу (хорошо, это довольно самоуверенно, но я надеюсь, что вы предоставите мне немного места для маневра). Givvy, действительно, является языком программирования, ориентированным на выражения — т.е. любой оператор на самом деле является выражением, которое возвращает значение — поэтому, например, используя REPL, вы можете начать с базовой программы с графическим интерфейсом и постепенно изменять ее итеративно, пока Вы получаете на стороне клиента желаемый результат.

Очевидно, что приложения Givvy совместимы во всех операционных системах, поддерживаемых JVM, поэтому, например, вы можете использовать Windows или MacOSX GVNode, взаимодействующий с Linux cNode, а также, напротив, Linux GVNode и Windows cNode. Любая комбинация разрешена.

Живи и Настольный теннис

Task ping
:(task pong, int count)

public function Pong()
if ( --count )
pong :@ Ping(this);
else
pong :@ Stop();
println "Ping finished";
this.finalize;
endelse
endfunc

pong :@ Ping(this);
close

Task pong
public function Ping(task ping)
ping :@ Pong();
endfunc

public function Stop()
println "Pong finished";
this.finalize;
endfunc
close

pong = &pong[];
&ping(pong, 100000)[];

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

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

На первый взгляд, если вы разработчик ОО, вы можете предположить, что Задача очень похожа на Класс , с методами и своего рода изменяемым идентификатором, поэтому

  • : (task pong, int count) выглядит как объявление
  • : @ оператор может быть способом вызова к другой задаче
  • this.finalize заявление разрушает Задачу
  • & [] оператор создает новый экземпляр Task

Но … поскольку Givvy не является языком программирования OO, задача может не быть классом.

Это все еще контейнер для данных и операций, но он не наследуется, вы не можете получить подклассы. На самом деле, задача в первую очередь «легкий процесс» ,

      –– Конечно, количество задач, которые можно создать, в основном зависит от объема памяти, который может адресовать JVM.

отличительной особенностью которого является возможность изменять его состояние или выполнять любые вычисления, которые он предоставляет, только через очередь (известную как почтовый ящик ), из которой все вызовы методов (в сообщениях реальности ), выдвигаемые из других задач, извлекаются и выполняются один за другим. один. Таким образом, никогда не бывает одновременного доступа к состоянию Задачи, в отличие от того, что может происходить в большинстве ОО-языков, поэтому нет необходимости в объектах защиты (блокировках) для ее защиты.

Это то, что вы обычно ожидаете от традиционной реализации модели актера . В Givvy, однако, он был разработан с совершенно иной философией, чем, например, принятые Erlang или Scala. Я упомяну здесь только некоторые концепции, которые характеризуют реализацию Givvy. Остальные, касающиеся Планировщиков , Маршрутизации сообщений и Ленивых Фьючерсов , будут обсуждаться в следующей статье.

  • Вышеупомянутые языки обычно опираются на структуру данных выбора и сопоставление с образцом для фильтрации сообщений для обработки, тогда как в Givvy каждое сообщение вызывает выполнение функции. Несмотря на функциональность, первый подход подразумевает, что программист не сможет применять одну и ту же логику при написании параллельных и неконкурентных программ (это в основном верно для Scala), и, кроме того, это может также восприниматься, на мой взгляд, как менее «естественный» для ОО-программиста, рассматривающего модель актера, по сравнению с подходом Гивви.
  • Givvy, кроме того, добавляет в модель новую структуру данных, известную как Core , которая представляет область выполнения группы задач. Действительно, любая задача всегда связана с конкретным ядром, которое также является контейнером и реальным супервизором почтового ящика, который получает сообщения, адресованные группе задач.

То, что в основном происходит внутри этой модели, это то, что

  1. сообщение — в форме вызова функции — отправляется задачей отправителя, скажем, в домене A , в почтовый ящик ядра, скажем, в домене B , где находится задача получателя
  2. Core B активизирует задачу получателя, когда обнаруживает, что в его почтовом ящике есть новое сообщение
  3. Задача получателя обрабатывает запрошенную работу и снова возвращается в неактивное состояние

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

В примере «Ping-Pong», напротив, отправка сообщений всегда асинхронна, как указано оператором : @ . Таким образом, ping не прерывает поток выполнения, ожидая ответа от pong .

    pong :@ Ping(this);

Однако даже в этом случае на производительность не оказывают такого негативного влияния, так как и здесь, при нересте, — обозначается оператором &

    pong = &pong[];
&ping(pong, 100000)[];

Обе задачи связаны с общим доменом, SysCore .

      –– SysCore, как правило, может быть опущен в синтаксисе [] , поскольку он уже создан для нас системой. В противном случае вы могли бы также написать

    [ "SysCore" ]

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


Быстро по оставшимся пунктам:

  • Только функции Задачи, объявленные как публичные, имеют глобальную видимость и поэтому могут вызываться из других Задач. Любая непубличная функция — то есть без публичного модификатора — представляет частную функцию Задачи; он может быть вызван только внутри своей области видимости.

        public function Pong(task pong)
  • Givvy, благодаря JVM, является сборщиком мусора , поэтому не обязательно вручную уничтожать каждую задачу, но оператор finalize может быть необходим для тех задач, содержащих общедоступные идентификаторы (например … Задачи, возвращающие ленивое будущее, всегда автоматически GCed, так как они не имеют какого-либо публичного идентификатора).

  • Синтаксис :() представляет инъекцию (у Givvy нет конструкторов ).

        :(task pong, int count)

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

    The injections accordingly do not have a body. They do not require code since their job is simply to initialize one or more Task’s identifiers. Nevertheless, every section of code outside any of the Task’s functions, i.e. at Task scope level, is executed at spawning time and then never again. This particular code is known as spawn code.


We still miss several features of the [] operator,

    pong = &pong[];
&ping(pong, 100000 - 1)[];

but I’d say it’s so prominent in Givvy to deserve an ad-hoc post. For the moment it’s enough to say that any Task is always scheduled by the programmer. When we talk about Schedulers, I’ll show you the advantages of this architectural decision. However, those eager can still have a look at this short presentation the summarizes all the features provided by the Givvy’s concurrency model.