Pr

Processus en Elixir

5 exercices

À propos de Processus

En Elixir, tout le code s'exécute dans des processus. Les processus Elixir :

  • Ne doivent pas être confondus avec les processus système.
  • Sont légers.
  • Ont des cas d'utilisation précis. Ils peuvent :
  • Il est normal d'avoir une application Elixir qui fait tourner des centaines de processus, mais aussi une qui ne crée pas explicitement de nouveaux processus, surtout s'il s'agit d'une bibliothèque.

Crée des processus

Par défaut, une fonction s'exécute dans le même processus que celui qui l'a appelée. Quand tu as besoin d'exécuter explicitement une fonction dans un nouveau processus, utilise spawn :

  • spawn/1 accepte une fonction qu'il exécutera directement.

    spawn(fn -> 2 + 2 end)
    # => #PID<0.125.0>
    
  • spawn/3 accepte un nom de module, un nom de fonction (sous forme d'atome) et la liste des arguments à passer à cette fonction, qu'il exécutera.

    spawn(String, :split, ["hello there", " "])
    # => #PID<0.113.0>
    
    • Ce triplet de données est souvent appelé un MFA, c'est-à-dire Module, Function, Arguments.
  • Un processus se termine dès que sa fonction a fini de s'exécuter.

  • Tu peux vérifier si un processus est toujours vivant (en cours d'exécution) avec Process.alive?/1 :

    pid = spawn(fn -> 2 + 2 end)
    Process.alive?(pid)
    # => false
    

Messages

Les processus ne partagent pas directement d'informations entre eux. Les processus envoient des messages pour partager des données. Ce modèle de concurrence s'appelle le modèle d'acteur.

  • Envoie des messages à un processus avec send/2.

    send(pid, :hello)
    
    • Le message arrive dans la boîte aux lettres du destinataire, dans l'ordre où ils sont envoyés.
    • send ne vérifie ni si le message a été reçu, ni si le destinataire est toujours vivant.
  • Un message peut être de n'importe quel type.

  • Tu peux recevoir un message envoyé au processus courant avec receive/1.

    • Tu dois faire correspondre les messages à des motifs.
    • receive attend qu'un message correspondant à l'un des motifs donnés se trouve dans la boîte aux lettres du processus.
      • Par défaut, il attend indéfiniment, mais on peut lui donner un délai d'attente avec un bloc after.
    • Les messages lus sont retirés de la boîte aux lettres du processus. Les messages non lus y restent indéfiniment.
      • Écris toujours une clause _ qui attrape tout dans receive/1, pour éviter de manquer de mémoire à cause de l'accumulation de messages non lus.
    receive do
      {:ping, sender_pid} -> send(sender_pid, :pong)
      _ -> nil
    after
      5000 ->
        {:error, "No message in 5 seconds"}
    end
    

Boucle de réception

Si tu veux recevoir plus d'un message, tu dois appeler receive/1 de façon récursive. Il est courant d'implémenter une fonction récursive, nommée par exemple loop, qui appelle receive/1, fait quelque chose avec le message, puis s'appelle elle-même pour attendre d'autres messages. Si tu as besoin de conserver un état d'un appel de receive/1 à un autre, tu peux le faire en passant un argument à cette fonction 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

En pratique, cette approche est rarement utilisée directement. Elixir propose des abstractions de concurrence, comme le module Agent ou le comportement GenServer, qui reposent tous deux sur la boucle de réception. Cependant, il est essentiel de comprendre ces bases pour pouvoir utiliser efficacement ces abstractions.

Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Processus

L'entraînement est verrouillé

Déverrouille 4 exercices de plus pour t'entraîner sur Processus