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 и поместите все сохраненные сообщения обратно в почтовый ящик в том порядке, в котором они прибыли в процесс.