У Elixir увесь код виконується всередині процесів. Процеси в Elixir:
Типово функція виконується в тому самому процесі, з якого її викликали. Коли потрібно явно запустити певну функцію в новому процесі, скористаймося spawn:
spawn/1 приймає функцію, яку він виконає безпосередньо.
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 приймає функцію, яку він виконає за назвою модуля, назвою функції (як атом) і масивом аргументів, що передаються цій функції.
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
Процес завершується, щойно його функція закінчує виконання.
Перевірити, чи процес усе ще живий (виконується), можна за допомогою Process.alive?/1:
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
Процеси не обмінюються інформацією безпосередньо один з одним. Щоб поділитися даними, процеси надсилають повідомлення. Цю модель конкурентності називають модель акторів.
Надсилати повідомлення процесу можна за допомогою send/2.
send(pid, :hello)
send не перевіряє, чи повідомлення отримано, і чи отримувач усе ще живий.Повідомлення може бути будь-якого типу.
Отримати повідомлення, надіслане поточному процесу, можна за допомогою receive/1.
receive чекає, поки в поштовій скриньці процесу зʼявиться повідомлення, що відповідає будь-якому із заданих шаблонів.
after.receive/1 універсальну умову _, щоб уникнути вичерпання памʼяті через накопичення непрочитаних повідомлень.receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
Якщо потрібно отримати більше ніж одне повідомлення, receive/1 доведеться викликати рекурсивно. Поширений прийом - реалізувати рекурсивну функцію, наприклад із назвою loop, яка викликає receive/1, щось робить із повідомленням, а потім викликає саму себе, щоб чекати на нові повідомлення. Якщо потрібно перенести якийсь стан з одного виклику receive/1 до іншого, це можна зробити, передавши аргумент у ту функцію loop.
def loop(state) do
receive do
:increment_by_one ->
loop(state + 1)
{:report_state, sender_pid} ->
send(sender_pid, state)
loop(state)
:stop ->
nil
_ ->
loop(state)
end
end
На практиці цей підхід рідко використовують напряму. Elixir пропонує абстракції конкурентності, як-от модуль Agent або поведінку GenServer, які обидві спираються на цикл отримання. Але вкрай важливо розуміти ці основи, щоб ефективно користуватися абстракціями.