Pr

Processi in Elixir

5 esercizi

Informazioni su Processi

In Elixir, tutto il codice viene eseguito all'interno di processi. I processi di Elixir:

  • Non vanno confusi con i processi di sistema.
  • Sono leggeri.
  • Hanno casi d'uso specifici. Possono:
  • È normale avere un'applicazione Elixir che esegue centinaia di processi, ma anche una che non crea affatto nuovi processi in modo esplicito, soprattutto se è una libreria.

Creare processi

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>
    
    • Questa terna di dati è spesso chiamata MFA, cioè Module, Function, Arguments.
  • 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
    

Messaggi

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)
    
    • Il messaggio finisce nella mailbox del destinatario nell'ordine in cui viene inviato.
    • 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.

    • Devi fare pattern matching sui messaggi.
    • receive attende finché nella mailbox del processo non c'è un messaggio che corrisponde a uno dei pattern indicati.
      • Per impostazione predefinita attende all'infinito, ma è possibile assegnargli un timeout usando un blocco after.
    • I messaggi letti vengono rimossi dalla mailbox del processo. I messaggi non letti vi restano all'infinito.
      • Scrivi sempre una clausola _ 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
    

Ciclo di ricezione

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.

Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Processi

La pratica è bloccata

Sblocca 4 altri esercizi per esercitarti su Processi