In Elixir, tutto il codice viene eseguito all'interno di processi. I processi di Elixir:
Per impostazione predefinita, una funzione viene eseguita nello stesso processo da cui è stata chiamata. Quando devi eseguire una determinata funzione in un nuovo processo in modo esplicito, usa spawn:
spawn/1 accetta una funzione che eseguirà direttamente.
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 accetta una funzione che eseguirà indicando il nome del modulo, il nome della funzione (come atomo) e un array di argomenti da passare a quella funzione.
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
Un processo termina non appena la sua funzione ha finito di essere eseguita.
Puoi verificare se un processo è ancora vivo (in esecuzione) con Process.alive?/1:
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
I processi non condividono direttamente le informazioni tra loro. I processi inviano messaggi per condividere dati. Questo pattern di concorrenza è chiamato modello ad attori.
Invia messaggi a un processo con send/2.
send(pid, :hello)
send non controlla se il messaggio è stato ricevuto né se il destinatario è ancora vivo.Un messaggio può essere di qualsiasi tipo.
Puoi ricevere un messaggio inviato al processo corrente con receive/1.
receive attende finché nella mailbox del processo non c'è un messaggio che corrisponde a uno dei pattern indicati.
after._ che cattura tutto in receive/1, per evitare di esaurire la memoria a causa dei messaggi non letti che si accumulano.receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
Se vuoi ricevere più di un messaggio, devi chiamare receive/1 in modo ricorsivo. È un pattern comune implementare una funzione ricorsiva, ad esempio chiamata loop, che chiama receive/1, fa qualcosa con il messaggio e poi chiama se stessa per attendere altri messaggi. Se devi trasportare uno stato da una chiamata a receive/1 all'altra, puoi farlo passando un argomento alla funzione 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
In pratica, questo approccio è usato raramente in modo diretto. Elixir offre astrazioni di concorrenza, come il modulo Agent o un comportamento GenServer, che si basano entrambe sul ciclo di ricezione. Tuttavia, è fondamentale capire queste basi per poter usare le astrazioni in modo efficiente.