- Процеси в Erlang - как и защо? Малко история.
- Създаване на процеси
- Конкурентност и паралелизъм
- Комуникация между процеси
- Erlang е създаден в лаборатория на Ericsson през 80-те години.
- Основната му идея е да е способ за писане на конкурентни програми, които трябва да могат да се изпълняват безкрайно.|
- Конкурентни (едно устройство трябва да може да поддържа хиляди едновременни транзакции).
- Толерантни към грешки и проблеми, както софтуерни, така и хардуерни.|
- Практически нулев downtime.|
- Кодът им да може да се заменя с по-нови версии, докато те работят.|
- Erlang е трябвало да бъде нещо като PLEX.
- Но да върви на различни типове хардуер.|
- И да е по-бърз и лесен за писане.|
- Множество паралелни процеси живеят в паметта.
- По всяко време, повечето от тях чакат събитие.|
- Когато събитие се случи, процесът прави някакво изчисление, променя си състоянието или изпраща съобщение след което пак чака.|
- Процесите трябва да са много леки и лесни за създаване.
- Процесите трябва да са част от самия език.|
- Грешките в един процес не могат да влияят на другите процеси.|
- По онова време паралелизъм означава множество устройства, които работят с дадения софтуер и се възприемат като едно.
- Тоест езикът трябва да е лесен за дистрибутивност.
- Кодът върви в процеси, които са на ниво език.
- Тези процеси не споделят памет - имат собствен стек и собствен heap.
- Много са лесни за създаване и си комуникират чрез размяна на съобщения.
- Лесно могат да си комуникират помежду си, дори да са на различни машини.
- Ако един процес 'умре', другите продължават да живеят. Може нов да го замести, зависи от стратегията.
- Erlang започва като библиотека на Prolog за fault-tolerant and distributed програмиране.
- Развива бързо като диалект на пролог.|
- Първият интерпретатор на езика е на Prolog.|
- Erlang е повлиян донякъде и от Smalltalk.|
- Erlang не е повлиян от и не имплементира Actor модела.
- Процесите на Erlang и актьорите имат общ предшественик - комуникацията между обекти със съобщения.|
- Доста от идеите за Актьорите намират своят път в процесите на Erlang независимо от Actor модела. |
- Вътрешността на един процес няма нищо общо с Actor модела. |
- Erlang се превръща от Prolog, който поддържа конкурентност в обособен език.
- Започва да работи още един човек - Robert Virding.|
- Двамата с Joe Armstrong оформят паралелно два интерпретатора на Erlang, написани на Prolog.|
- Erlang придобива потребители - 3-ма човека.|
- Процесите започват да имат специален буфер, наречен 'кутия за съобщения' Mailbox.
- Започват да могат да създават връзки помежду си.|
- Ако някой от тях получи грешка, друг може да бъде уведомен със специално съобщение и да реагира.|
- В края на 1989 година, езикът е тестван и функционалността му е намерена за задоволителна.
- Проблемът е че е много бавен.
- Излиза ново изискване - да го направят поне 40 пъти по-бърз, което после се увеличава.
- Ражда първата абстрактна машина на Erlang, написана на C - JAM.
- Преди C и други езици са разглеждани, и други абстрактни машини са разучавани.|
- Mike Williams е с доста повече опит от Joe в C, затова той написва JAM.|
- 90-те години бележат началото на изхвърляне на доста Prolog синтаксис от Erlang
- По добри GC стратегии.
- Binary информация над даден размер да се пази в общ heap за даден node
- ...
- През 1993 Bogumil (Bogdan) Hausman създава TEAM, прекръстена после на BEAM.
- Доста по-оптимизирана машина за изпълнение на Erlang bytecode.
Две хубави неща се случват за Erlang:
- В края на 1995, проекта за AXE-N устройствата се сгромолясва.
- През 1998 година Ericsson Radio AB забранява Erlang за ползване.
- Множество малки помощни библиотеки на Erlang.
- Design Patterns за програмиране на често желани програми.
- Документация, курсове и How to-та
- Mnesia/ETS бази данни
Joe Armstrong нарича Erlang език за конкурентно-ориентирано програмиране, като се базира на няколко правила.
- Системата е изградена от процеси.
- Процесите не споделят нищо.|
- Процесите си комуникират чрез асинхронно изпращане на съобщения.|
- Процесите са изолирани един от друг.|
- 1986 : Erlang е декларативен език с добавена способност за конкурентно изпълнение.
- 1995 : Erlang е функционален език с добавена способност за конкурентно изпълнение.
- 2005 : Erlang е конкурентно-ориентиран език, който се състои от комуникиращи си компоненти, написани на функционален език.
spawn(fn ->
<изрази>
end)execute_after_action = fn (action, milliseconds) ->
:timer.sleep(milliseconds)
result = action.()
IO.puts(result)
endexecute_after_action.(fn -> "Awake!" end, 1000)spawn(fn -> execute_after_action.(fn -> "Awake!" end, 1000) end)
IO.puts("Sleeping...")Друга форма на spawn е spawn/3. Тази функция има три аргумента, често наричани MFA.
defmodule Executor do
def action_after(action, milliseconds) do
:timer.sleep(milliseconds)
IO.puts(action.())
end
end
spawn(Executor, :action_after, [fn -> "Finally!" end, 1000])- Когато стартираме Elixir, той върви в един OS process или една BEAM инстанция, която наричаме node.
- За всяко ядро на CPU-то си, обикновено получаваме по една OS-level нишка.|
- Във всяка такава нишка се изпълнява нещото, наречено Scheduler.|
- Scheduler-ите обикновено са обвързани с ядро на процесора, но е възможно и да ги сменят.|
- Един Scheduler управлява опашка, наречена run queue.
- Като цяло това е приоритетна опашка от Elixir процеси и портове.|
- Това значи че ако имаме четири Scheduler-а, е възможно да имаме четири паралелни Elixir-level процеса.|
- При spawn се създава процес и се поставя в някоя от опашките.
- Един Elixir-level процес е голям около 1KB-2KB при създаването си.|
- Можем да създаваме огромен брой процеси без да се притесняваме. Говорим за милиони.|
- Процесите имат право на до N редукции.
- Всяка операция свързана с процес е редукция.|
- Когато текущо-изпълняващ се процес изчерпа редукциите си или пък е в очакване на нещо и не прави нищо, той става неактивен.|
- По тежките операции са по скъпи.|
- Възможно е процес да смени опашката си и да започне да се управлява от друг Scheduler.
- Има сложен алгоритъм за балансиране на натоварването между ядрата.|
- Често ако Scheduler остане без работа може да си 'поиска' процеси.|
Има три основни функции за работа с процеси:
- spawn ги създава.
- send изпраща съобщение до процес.
- receive чака за съобщения към текущия процес.
pid = spawn(action)
send(pid, message)pid = spawn(fn ->
receive do
pattern1 -> action1
pattern2 -> action2
....
patternN -> actionN
end
end)
send(pid, pattern2)@[1-8] @[10] @[2-7]
pid = spawn(fn ->
receive do
:say_hi -> IO.puts("Hi!")
:say_bye -> IO.puts("Bye!")
{:say, name, msg} -> IO.puts([name, " says ", msg])
end
end)
send(pid, {:say, "Arnold", "I'll be back!"})@[1-7] @[9] @[2-6] @[5]
- Receive е като case, който се изпълнява върху полученото съобщение.
- Съобщението може да е всякакъв тип.
- Можем да изпратим
PID-а на процеса, който извикваsendи да го използваме за да получим отговор.
pid = spawn(fn ->
receive do
{sender, :ping} when is_pid(sender) ->
send(sender, {self(), :pong})
end
end)
send(pid, {self(), :ping})
IO.puts("Let's wait for a pong!")
receive do
{sender, :pong} when is_pid(sender) ->
IO.puts([inspect(sender), " sends PONG!"])
end@[1-6] @[8] @[2-5] @[3] @[11-14]
@[4] @[12]
Паралелен Enum.map без да ползваме Task

1..50 |> Enum.map(fn x -> x * x end)defmodule PEnum do
def map(enumerable, map_func) do
enumerable
|> Enum.map(spawn_func(map_func))
|> Enum.map(&receive_func/1)
end
end@[4] @[5]
defp spawn_func(map_func) do
current_pid = self()
fn x ->
spawn(fn ->
send(current_pid, {self(), map_func.(x)})
end)
end
end
defp receive_func(pid) do
receive do
{^pid, result} -> result
end
end@[1-8] @[3] @[4-6] @[5] @[2] @[5]
@[10-14] @[11-13] @[12]
1..50 |> Enum.map(fn x -> :timer.sleep(1000); x* x end)
# След около 50 секунди ще имаме квадратите
1..50 |> PEnum.map(fn x -> :timer.sleep(1000); x* x end)
# След около секунда ще имаме квадратите- Процесите в Elixir са много леки и лесни за употреба
- Не споделят данни.
- Могат да се изпълняват паралелно.

























