En Elixir, tout le code s'exécute dans des processus. Les processus Elixir :
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>
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
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)
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.
receive attend qu'un message correspondant à l'un des motifs donnés se trouve dans la boîte aux lettres du processus.
after._ 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
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.