Статьи

Введение в язык программирования JR

JR — это язык программирования, специально созданный для решения параллельных задач программирования. Этот язык расширяет Java, добавляя основные парадигмы параллельного программирования. Более того, JR упрощает концепции, реализованные в Java как процессы или семафоры. Есть также несколько расширений для JR, чтобы добавить больше функций, таких как мониторы и условная критическая область (CCR). JR является реализацией языка SR для Java. JR просто добавляет слой поверх Java. Как только мы используем компилятор JR, исходные файлы JR преобразуются в файлы Java и выполняются виртуальной машиной, как и любой другой класс Java.

JR часто используется в качестве школьной поддержки для изучения параллельного программирования. В этой статье мы увидим основы программирования с помощью JR. Представленная версия с июня 2009 года, версия 2.00602, которая основана на Java 6.0.

В этой статье необходимо, чтобы в вашей системе была установлена ​​среда JR. Статья доступна здесь для установки под Windows.

В этой статье мы уделим особое внимание частям языка JR для параллельного программирования. Мы не увидим целостности языка. У JR есть и другие преимущества, кроме упрощения параллельного программирования, но мы не увидим этого в этой статье. Более того, все аспекты параллельного программирования в JR здесь не рассматриваются.

Привет мир

Как и все другие языки, мы должны начать с простого Hello World. Таким образом мы создадим файл Hello.jr. Ничего особенного, это чистая Java:

public class Hello {
public static void main(String[] args){
System.out.println("Hello World");
}
}

Тогда мы можем скомпилировать это:

jrc Hello.jr

Это создаст папку jrGen, содержащую файлы Java. Результатом компиляции JR всегда является набор файлов Java, соответствующих переводу файлов JR.

Чтобы запустить вашу JR-программу, используйте команду jr, за которой следует имя основного класса (класс, содержащий метод main):

jr Hello

Это будет отображать:

Hello World

Команда jr также запустит компиляцию файлов Java. Эта подборка будет сделана каждый раз. Если вы хотите запускать только скомпилированные файлы, вы можете использовать команду jrrun.

Как сказано во введении, язык JR расширяет язык Java. Таким образом, вы можете кодировать на Java с помощью JR. Таким образом, Hello World — это только Java.

Процессы

Первое, что нужно увидеть, — это объявление процессов. Это делается проще, чем в Java. Не нужно создавать экземпляры некоторых объектов, это делается декларативным способом, а JR делает все остальное.

Для объявления процесса JR вводит новое ключевое слово process, которое позволяет объявлять процесс. Вот самое простое объявление процесса:

process Hello {
System.out.println("Processus");
}

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

public class HelloProcess {
static process Hello {
System.out.println("Hello World");
}

public static void main(String[] args){}
}

который отображает то же самое, что и первая версия Hello World. Разница в том, что наш дисплей сделан из потока.

Кроме того, JR позволяет вам объявлять большой набор потоков в объявлении со следующим синтаксисом:

static process Hello((int id = 0; id < n; id++)){}

Это объявит n потоков. Синтаксис такой же, как в цикле for. Давайте объявим 25 потоков Hello World:

public class HelloProcess {
static process Hello((int id = 0; id < 25; id++)){
System.out.println("Hello World from thread " + id);
}

public static void main(String[] args){}
}

Когда мы запустим это, мы можем получить следующий результат:

Hello World from thread 2
Hello World from thread 24
Hello World from thread 11
Hello World from thread 22
Hello World from thread 0
Hello World from thread 4
Hello World from thread 6
Hello World from thread 8
Hello World from thread 10
Hello World from thread 12
Hello World from thread 14
Hello World from thread 16
Hello World from thread 18
Hello World from thread 20
Hello World from thread 23
Hello World from thread 21
Hello World from thread 19
Hello World from thread 17
Hello World from thread 15
Hello World from thread 13
Hello World from thread 9
Hello World from thread 7
Hello World from thread 5
Hello World from thread 3
Hello World from thread 1

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

Действие покоя

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

До этого мы привыкли вводить понятие операций. В нашем случае операция — это простой метод, объявленный с ключевым словом op. Но в JR операция — это больше, чем метод, и ее можно вызывать различными способами и разрешать другие методы, но это выходит за рамки данной статьи.

Вот объявление простой операции.

public static op void end(){
System.out.println("End");
}

Итак, это простой метод с префиксом op. Вы можете вызвать его как любой другой метод. Но вы также можете объявить это как действие, которое выполняется, когда система находится в состоянии покоя. Если мы возьмем наш пример с 25 привет миром и если мы определим действие покоя, то вот что мы получаем:

import edu.ucdavis.jr.JR;

public class QuiescenceProcess {
static process Hello((int id = 0; id < 25; id++)){
System.out.println("Hello World from thread " + id);
}

public static void main(String[] args){
try {
JR.registerQuiescenceAction(end);
} catch (edu.ucdavis.jr.QuiescenceRegistrationException e){
e.printStackTrace();
}
}

public static op void end(){
System.out.println("End");
}
}

Мы используем метод registerQuiescenceAction (op) класса JR. Этот класс предоставляет некоторые служебные методы для программ JR.
При запуске мы видим нечто подобное:

Hello World from thread 0
Hello World from thread 22
Hello World from thread 23
Hello World from thread 21
Hello World from thread 24
Hello World from thread 20
Hello World from thread 19
Hello World from thread 18
Hello World from thread 17
Hello World from thread 16
Hello World from thread 15
Hello World from thread 14
Hello World from thread 13
Hello World from thread 12
Hello World from thread 11
Hello World from thread 10
Hello World from thread 9
Hello World from thread 8
Hello World from thread 7
Hello World from thread 6
Hello World from thread 5
Hello World from thread 4
Hello World from thread 3
Hello World from thread 2
Hello World from thread 1
End

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

 

 

семафоры

Теперь мы увидим, как использовать одну из основных концепций параллельного программирования: семафоры. Семафоры — это действительно простая концепция, но очень мощная. Семафор представляет собой определенное целочисленное значение, представляющее количество потоков, которые могут пройти в определенной части кода, назовите его «s». Семафор имеет два действия:

  • P : заставить поток ждать, пока s равно 0, а затем уменьшить s.
  • V : приращение с.

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

Объявление и использование семафоров действительно просты. Вот как вы можете объявить семафор с начальным значением 1:

sem mutex  = 1;

Тогда операции P et V чрезвычайно просты в использовании:

P(mutex);
//Critical section
V(mutex);

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

private static int value = 0;

static process Calculator((int id = 0; id < 50; id++)){
for(int i = 0; i < 5; i++){
value = value + 2;
}
}

Поскольку этот код запускает 50 потоков, каждый из которых добавляет 5 к 2, мы можем подумать, что значение должно быть 500 в конце выполнения, не так ли?

Но ничто не гарантирует этого результата. Это известно как чередование в параллельном программировании. Это связано с тем, что в действительности операция + 2 — это 3 операции:

  • прочитать значение стоимости
  • добавить 2 к прочитанному значению
  • установить новое значение в значение

Поток может быть помещен в ожидание сразу после 1 и выполнить приращение для старого значения, но другие потоки все еще сделали приращение, но у него есть старое значение, когда он делает +2 и запись и ложное значение в значение. Чтобы доказать это вам, выполните следующий код несколько раз:

import edu.ucdavis.jr.JR;

public class SemaphoreProcess {
private static int value = 0;

static process Calculator((int id = 0; id < 50; id++)){
for(int i = 0; i < 5; i++){
value = value + 2;
}
}

public static void main(String[] args){
try {
JR.registerQuiescenceAction(end);
} catch (edu.ucdavis.jr.QuiescenceRegistrationException e){
e.printStackTrace();
}
}

public static op void end(){
System.out.println(value);
}
}

На моем компьютере у меня есть следующие результаты.

498
500
500
500
496

И если мы используем более высокие значения, это еще хуже. Например, с 100 потоками и 100 итерациями:

20000
19560
19912
19758
20000

Но эту проблему можно решить с помощью семафоров:

private static sem mutex = 1;
private static int value = 0;

static process Calculator((int id = 0; id < 50; id++)){
for(int i = 0; i < 5; i++){
P(mutex);
value = value + 2;
V(mutex);
}
}

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

Вывод

Итак, теперь мы раскрываем основные понятия языка программирования JR. Как вы видели в этой статье, этот язык программирования облегчает параллельные концепции программирования.

Я надеюсь, что эта статья была полезна для вас, чтобы открыть язык программирования JR, и почему бы не изучить и использовать этот язык.

С http://www.baptiste-wicht.com/2010/01/jr-introduction/