Em Elixir, todo o código corre dentro de processos. Os processos em Elixir:
Por predefinição, uma função é executada no mesmo processo a partir do qual foi chamada. Quando precisas de executar uma determinada função num novo processo, usa spawn:
spawn/1 aceita uma função que executa diretamente.
spawn(fn -> 2 + 2 end)
# => #PID<0.125.0>
spawn/3 aceita o nome do módulo, o nome da função (como átomo) e uma lista de argumentos a passar a essa função.
spawn(String, :split, ["hello there", " "])
# => #PID<0.113.0>
Um processo termina assim que a sua função acaba de ser executada.
Podes verificar se um processo ainda está vivo (a executar) com Process.alive?/1:
pid = spawn(fn -> 2 + 2 end)
Process.alive?(pid)
# => false
Os processos não partilham informação diretamente entre si. Os processos enviam mensagens para partilhar dados. Este padrão de concorrência chama-se modelo de atores.
Envia mensagens a um processo com send/2.
send(pid, :hello)
send não verifica se a mensagem foi recebida nem se o destinatário ainda está vivo.Uma mensagem pode ser de qualquer tipo.
Podes receber uma mensagem enviada ao processo atual com receive/1.
receive espera até haver na caixa de correio do processo uma mensagem que corresponda a algum dos padrões indicados.
after._ que apanhe tudo no receive/1 para evitares ficar sem memória devido ao acumular de mensagens não lidas.receive do
{:ping, sender_pid} -> send(sender_pid, :pong)
_ -> nil
after
5000 ->
{:error, "No message in 5 seconds"}
end
Se quiseres receber mais do que uma mensagem, tens de chamar o receive/1 de forma recursiva. É um padrão comum implementar uma função recursiva, por exemplo chamada loop, que chama o receive/1, faz algo com a mensagem e depois se chama a si própria para esperar por mais mensagens. Se precisares de transportar algum estado de uma chamada ao receive/1 para outra, podes fazê-lo passando um argumento a essa função 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
Na prática, esta abordagem raramente é usada diretamente. O Elixir oferece abstrações de concorrência, como o módulo Agent ou o comportamento GenServer, que assentam ambos no ciclo de receção. No entanto, é crucial compreender estes conceitos básicos para conseguires usar as abstrações de forma eficiente.