Comunicação entre procesos em Elixir
Tags: elixir, process, otp, begginer
Published: 2021-10-07 22:16:18

O foco dessa postagem é explicar a comunicação entre processos em elixir de forma simples em português. Então toda a postagem e os exemplos de código estão nessa língua. Você pode ler a versão em inglês [aqui](https://dev.to/thiagoslima/process-communication-in-elixir-3hg). Quando terminar de ler você terá um entendimento básico de como criar processos e como eles enviam e recebem mensagens.

## Como criar processos

Um processo é algo que pode fazer coisas de forma isolada e concorrente. Bem... isso ficou muito vago. Vamos tentar um exemplo mais concreto. Digamos que você seja um rato. Você pode fazer várias coisas que os ratos fazem, como se esconder ou comer queijo por exemplo. Você é independente, não precisa de outras pessoas para fazer o que quer e eles não interferem nas suas atividades. Ou seja, você faz isso de forma isolada. Em um concurso de ratos para ver qual come mais queijo, todos poderiam fazer isso de forma concorrente e paralela. Todos comem ao mesmo tempo e não um de cada vez.

![mouse](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3adp7er65d6a5kn2tomf.jpg)

Agora que temos uma noção do que é um processo, vamos imaginar que você quer fazer um novo amigo, ou seja, outro processo. Então vamos criar o processo gato. É possível fazer isso com a função `spawn`. 

![spawn a process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fua21fvbyevdylmn7w9.jpg)

A função `spawn` cria um novo processo e executa uma função nele. Ela tem duas aridades: `spawn/1` recebe uma função e `spawn/3` recebe um módulo, uma função como um átomo e uma lista de parâmetros. Aqui vamos utilizar a segunda forma porque vamos definir o `Gato` em um módulo. A função `criar` apenas imprime uma mensagem. Você pode colocar o código abaixo em um arquivo chamado `gato.exs` e executar com `elixir gato.exs`. Se fizer isso vai ver o resultado comentado.

```elixir
# gato.exs
defmodule Gato do
  def criar do
    IO.puts("Você criou um gato")
  end
end

# Cria um gato
spawn(Gato, :criar, [])

# Resultado:
# "Você criou um gato"
```

## Comunicação entre processos

Depois de criar um novo processo, seu amigo o gato, você quer falar com ele. Mas lembre-se que os processos são isolados. Imagine que cada um vive em sua própria casa com distanciamento social. Então como se comunicar com ele? Há milhões de anos atrás as pessoas utilizavam um forma de comunicação chamada correio. Os processos se comunicam de forma parecida. Então para falar com seu amigo você vai enviar uma mensagem desse jeito.

![send receive messages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgenp3gvwnumebmhsmqz.jpg)

### Enviando e recebendo mensagens

Enviamos uma mensagem com a função `send/2`. Passamos para essa função duas coisas: o **destinatário** da mensagem e qual é a **mensagem**. Mas como nosso código vai saber enviar a mensagem para o processo gato? Ainda bem que a função `spawn` retorna pra  gente um identificador do processo criado, como se fosse seu endereço. Então vamos salvar o retorno de `spawn` na variável gato. Agora é só enviar a mensagem.

O gato precisa saber como receber essa mensagem. Para isso a gente definiu o bloco `receive`. Funciona assim: o gato vai olhar dentro da caixa de correio e decidir o que é importante. Ou seja, para quais mensagens ele quer fazer alguma coisa.

```elixir
# gato.exs
defmodule Gato do
  def criar do
    receive do
      :e_ai_beleza -> IO.puts("De boas")
    end
  end
end

# Cria um gato
gato = spawn(Gato, :criar, [])

# Manda mensagem
send(gato, :e_ai_beleza)

# Resultado:
# "De boas"
```

### Respondendo de volta

Nosso código tem um problema. Imagine o `IO.puts("De boas")` como se o gato tivesse falado isso. Mas vocês estão em suas próprias casas, lembra? Então você não ouviu a resposta dele. Como os processos se comunicam com mensagens, ele teria que te mandar uma mensagem de volta.

![answer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pks88rxh5gkxmfztouvz.jpg)

Ainda bem que você já sabe como enviar uma mensagem. É só fazer o gato responder com `send(rato, :de_boas)` e está tudo certo! Entretanto como o gato vai saber que a mensagem veio do rato? Para fazer isso, vamos falar na mensagem quem foi o `remetente`. Temos que mudar o `receive` do gato para se importar com mensagens que seguem o padrão `{:e_ai_beleza, remetente}`. Assim ele pode responder para a pessoa certa com `send(remetente, :de_boas)`. Fica assim:

```elixir
receive do
  {:e_ai_beleza, remetente} -> send(remetente, :de_boas)
end
```

Agora o rato precisa se identificar para mandar a mensagem `send(gato, {:e_ai_beleza, rato})`. Lembre-se que o rato é o processo atual. Para identificar o processo atual no código usamos a função `self/0`. É como se estivéssemos pegando nosso próprio endereço. Então salvamos seu retorno em uma variável `rato = self()`.

```elixir
# Esse é você
rato = self()

# Manda mensagem
send(gato, {:e_ai_beleza, rato})
```

Está quase tudo pronto. Só precisamos ver na caixa de correio do rato a mensagem que o gato enviou e decidir o que fazer. O código todo ficaria assim:

```elixir
# gato.exs
defmodule Gato do
  def criar do
    receive do
      {:e_ai_beleza, remetente} -> send(remetente, :de_boas)
    end
  end
end

# Esse é você
rato = self()

# Cria um gato
gato = spawn(Gato, :criar, [])

# Manda mensagem
send(gato, {:e_ai_beleza, rato})

receive do
  :de_boas -> IO.puts("Massa!")
end

# Resultado
# Massa!
```


### Processo Temporário

É possível verificar se um processo está vivo com a função `Process.alive?/1`. Vamos executar o mesmo código acima mas ver quando o processo gato está vivo adicionando `IO.puts("Gato está vivo? #{Process.alive?(gato)}")`.

```elixir
defmodule Gato do
  def criar do
    receive do
      {:e_ai_beleza, remetente} -> send(remetente, :de_boas)
    end
  end
end

# Esse é você
rato = self()

# Cria um gato
gato = spawn(Gato, :criar, [])

IO.puts("Gato está vivo? #{Process.alive?(gato)}")

# Manda mensagem
send(gato, {:e_ai_beleza, rato})

receive do
  :de_boas -> IO.puts("Massa!")
end

IO.puts("Gato está vivo? #{Process.alive?(gato)}")

# Resultado
# Gato está vivo? true
# Massa!
# Gato está vivo? false
```

Podemos ver que o processo estava vivo, mas depois de responder ele morreu. Por que isso aconteceu?

![dead process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/la6scg2lkcfmh7833i98.jpg)

Para entender o que aconteceu vamos olhar a função `criar`. Aqui a gente definiu um bloco `receive`. Como o rato recebeu a mensagem de volta, a comunicação está correta. Mas o que acontece depois que o gato responde? Simplesmente não acontece mais nada no processo gato. Sendo assim, ele não precisa mais existir e o processo morre!

```elixir
def criar do
  receive do
    {:e_ai_beleza, remetente} -> send(remetente, :de_boas)
  end
end
```

### Processo Permanente

Para manter o processo vivo vamos dizer que ele não responde **apenas uma mensagem**. Ele vai ficar checando das mensagens e respondendo. Podemos fazer isso definindo a função `checar_mensagens/0` e passar o bloco `receive` para ela.

```elixir
def criar do
  checar_mensagens()
end

def checar_mensagens do
  receive do
    {:e_ai_beleza, remetente} -> send(remetente, :de_boas)
  end

  checar_mensagens()
end
```

Dessa forma a única coisa que a função `criar` faz é dizer para o gato checar as mensagens. Agora olhe a última linha de `checar_mensagens/0`. Ela chama a si mesma! Ou seja sempre que acabar ela volta para o começo. É uma função recursiva. Dessa forma o gato checa as mensagens para sempre e não morre. O código todo fica assim:

```elixir
defmodule Gato do
  def criar do
    checar_mensagens()
  end

  def checar_mensagens do
    receive do
      {:e_ai_beleza, remetente} -> send(remetente, :de_boas)
    end

    checar_mensagens()
  end
end

# Esse é você
rato = self()

# Cria um gato
gato = spawn(Gato, :criar, [])

IO.puts("Gato está vivo? #{Process.alive?(gato)}")

# Manda mensagem
send(gato, {:e_ai_beleza, rato})

receive do
  :de_boas -> IO.puts("Massa!")
end

IO.puts("Gato está vivo? #{Process.alive?(gato)}")

# Resultado
# Gato está vivo? true
# Massa!
# Gato está vivo? true
```

## Conclusão

Aqui você aprendeu o básico de como criar processos com `spawn`. Como eles enviam mensagens com `send/2` e recebem mensagens com `receive`. Você viu um pouco sobre como um processo pode morrer e como mantê-lo vivo. E o melhor de tudo, que gatos e ratos podem ser amigos no fim das contas!