Erlang - Параллелизм

Параллельное программирование в Erlang должно иметь следующие основные принципы или процессы.

Список включает следующие принципы -

piD = spawn (Развлечение)

Создает новый параллельный процесс, оценивающий Fun. Новый процесс выполняется параллельно с вызывающим. Пример выглядит следующим образом -

пример

-module(helloworld). 
-export([start/0]). 

start() ->
   spawn(fun() -> server("Hello") end). 

server(Message) ->
   io:fwrite("~p",[Message]).

Результат вышеупомянутой программы -

Вывод

“Hello”

Пид! Сообщение

Отправляет сообщение процессу с идентификатором Pid. Отправка сообщений асинхронная. Отправитель не ждет, а продолжает то, что делал.‘!’ называется оператором отправки.

Пример выглядит следующим образом -

пример

-module(helloworld). 
-export([start/0]). 
start() -> 
   Pid = spawn(fun() -> server("Hello") end), 
   Pid ! {hello}. 

server(Message) ->
   io:fwrite("~p",[Message]).

Получить… конец

Получает сообщение, отправленное процессу. Он имеет следующий синтаксис -

Синтаксис

receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
End

Когда сообщение поступает в процесс, система пытается сопоставить его с шаблоном 1 (с возможной защитой Guard1); если это удается, он оценивает Expressions1. Если первый шаблон не совпадает, он пробует Pattern2 и так далее. Если ни один из шаблонов не соответствует, сообщение сохраняется для дальнейшей обработки, и процесс ожидает следующего сообщения.

Пример всего процесса со всеми 3 командами показан в следующей программе.

пример

-module(helloworld). 
-export([loop/0,start/0]). 

loop() ->
   receive 
      {rectangle, Width, Ht} -> 
         io:fwrite("Area of rectangle is ~p~n" ,[Width * Ht]), 
         loop(); 
      {circle, R} ->
      io:fwrite("Area of circle is ~p~n" , [3.14159 * R * R]), 
      loop(); 
   Other ->
      io:fwrite("Unknown"), 
      loop() 
   end. 

start() ->
   Pid = spawn(fun() -> loop() end), 
   Pid ! {rectangle, 6, 10}.

В отношении вышеуказанной программы необходимо отметить следующее:

  • Функция цикла имеет цикл окончания приема. Поэтому, когда сообщение отправлено, оно будет обработано циклом приема.

  • Создается новый процесс, который переходит к функции цикла.

  • Сообщение отправляется порожденному процессу через Pid! команда сообщения.

Результат вышеупомянутой программы -

Вывод

Area of the Rectangle is 60

Максимальное количество процессов

В параллелизме важно определить максимальное количество процессов, разрешенных в системе. Тогда вы сможете понять, сколько процессов может выполняться одновременно в системе.

Давайте посмотрим на пример того, как мы можем определить максимальное количество процессов, которые могут выполняться в системе.

-module(helloworld). 
-export([max/1,start/0]). 

max(N) -> 
   Max = erlang:system_info(process_limit), 
   io:format("Maximum allowed processes:~p~n" ,[Max]), 
   
   statistics(runtime), 
   statistics(wall_clock), 
   
   L = for(1, N, fun() -> spawn(fun() -> wait() end) end), 
   {_, Time1} = statistics(runtime), 
   {_, Time2} = statistics(wall_clock), lists:foreach(fun(Pid) -> Pid ! die end, L), 
   
   U1 = Time1 * 1000 / N, 
   U2 = Time2 * 1000 / N, 
   io:format("Process spawn time=~p (~p) microseconds~n" , [U1, U2]).
   wait() -> 
   
   receive 
      die -> void 
   end. 
 
for(N, N, F) -> [F()]; 
for(I, N, F) -> [F()|for(I+1, N, F)]. 

start()->
   max(1000), 
   max(100000).

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

Maximum allowed processes:262144
Process spawn time=47.0 (16.0) microseconds
Maximum allowed processes:262144
Process spawn time=12.81 (10.15) microseconds

Получить с таймаутом

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

Ниже приведен синтаксис получаемого сообщения с указанным таймаутом.

Синтаксис

receive 
Pattern1 [when Guard1] -> 
Expressions1; 

Pattern2 [when Guard2] ->
Expressions2; 
... 
after Time -> 
Expressions 
end

Самый простой пример - создать функцию сна, как показано в следующей программе.

пример

-module(helloworld). 
-export([sleep/1,start/0]). 

sleep(T) ->
   receive 
   after T -> 
      true 
   end. 
   
start()->
   sleep(1000).

Приведенный выше код будет спать в течение 1000 мс перед фактическим выходом.

Выборочный прием

У каждого процесса в Erlang есть связанный почтовый ящик. Когда вы отправляете сообщение процессу, оно помещается в почтовый ящик. Единственный раз, когда этот почтовый ящик исследуется, - это когда ваша программа оценивает оператор приема.

Ниже приведен общий синтаксис оператора выборочного приема.

Синтаксис

receive 
Pattern1 [when Guard1] ->
Expressions1; 

Pattern2 [when Guard1] ->
Expressions1; 
... 
after 
Time ->
ExpressionTimeout 
end

Вот как работает приведенный выше оператор приема -

  • Когда мы вводим оператор приема, мы запускаем таймер (но только если в выражении присутствует секция after).

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

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

  • Если ни одно из сообщений в почтовом ящике не совпадает, процесс приостанавливается и будет перенесен на выполнение в следующий раз, когда в почтовый ящик будет помещено новое сообщение. Обратите внимание, что при поступлении нового сообщения сообщения в очереди сохранения не сопоставляются повторно; совпадает только новое сообщение.

  • Как только сообщение найдено, все сообщения, помещенные в очередь сохранения, повторно вводятся в почтовый ящик в том порядке, в котором они поступили в процесс. Если был установлен таймер, он очищается.

  • Если таймер истекает, когда мы ожидаем сообщения, оцените выражения ExpressionsTimeout и поместите все сохраненные сообщения обратно в почтовый ящик в том порядке, в котором они прибыли в процесс.