DEV Community

Mạnh Vũ
Mạnh Vũ

Posted on • Updated on

Elixir Process, what is? how work? What is linked & monitored process

One of some difficult to understand from other languages go to Elixir is process and people usually ignore it to use library.

Actually, Elixir process is very powerful thing, If go to work with large scale system we need to do with process a lot.

I have long time work with Erlang (also Golang) and process is one of most interesting thing for me.

From my view process in Elixir has 3 important things to care.

  1. Process is isolated code (like an island) & send/receive message is a way to interactive with outside world. If process is die, it doesn't affect to other processes (except one case is linked process).
  2. Process can be monitor to know how process is exited (:normal, :error,..).
  3. Process can be linked (group together) and all processes linked with failed process will be die (except only case is process turn trap_exit to true).

From 3 things above we can made a lot of thing with processes like: build a custom supervisor/worker model, simple make a pool processes to share workload, make a chain data processing,...

Now we go through one by one for understand Elixir process concept.

Isolated code in process

Work in Elixir much more easy if we understand how it works. From sharing variables (mutable variables) a lit of bit feel hard to bring algorithms (also real loop/while for Elixir). Just imagine you live alone on an island and just to one way to communicate with outside by put message to a bottle then ocean will care anything else.
(Actually, we can share state by :ets, :persistent_termor another database).

To start new a process we use spawn, spawn_link or spawn_monitor function. An other way to start a process is use Supervisor I will talk about this in other post.

Create a process:

spawn(fn ->
    sum = Enum.reduce(1..100, 0, fn n, acc -> acc + n end)
    IO.puts "sum: #{inspect sum}"
  end)
Enter fullscreen mode Exit fullscreen mode

After spawn (created) process will have a PID to identify for communicate or control. We can register a name (an atom) for process.

Register a name for process:

# Register name for current process
Process.register(self(), :my_name)

# Register name for other process.
Process.register(pid, :my_friend)
Enter fullscreen mode Exit fullscreen mode

When you want to send a message you put message with address to bottle then go to beach and throw bottle to ocean. With a little bit of time your friend you will receive a message.
And when you want to get a message from other islands you go to the beach and see if has any bottle in the beach, check then get only message you want to receive.

For action like server/client, we send a message and stay at the beach with time (or forever) to get result.

communicate between two processes

To send in code we use send/2 function:

send(pid, {:get, :user_info, self()})
Enter fullscreen mode Exit fullscreen mode

To receive message (already in mailbox or wait new one) we use receive do syntax just like case do:

receive do
      {:user_info, data} ->
        IO.puts "user data: #{inspect data}"
      other ->
        IO.puts "other data: #{inspect other}"
end
Enter fullscreen mode Exit fullscreen mode

You can put receive do to a function to go to loop again if you want to make process like a server.

In case message doesn't match any pattern in receive do it till stay in mailbox of process. You can get it in the future but need sure do not push a lot un matched message to process because it can make a OMM.

link processes

This feature is very powerful feature of Elixir. We can control a group of processes for do group tasks or chain of tasks.

Example:
Process A --linked--> B --linked--> C

IO.puts "I'm A"

fun = fn ->
  receive do
    :shutdown ->
      IO.puts "exited"
    {:ping, from} ->
      IO.puts "got a ping from #{inspect from}"
      send(from, :pong)
    message ->
      IO.puts "Got a message: #{inspect message}"
  end
end

# spawn and link process B
spawn_link(fn ->
  IO.puts "I'm B"
  spawn_link(fn ->
    IO.puts "I'm C"

    fun.()
  end)

  fun.()
end)
Enter fullscreen mode Exit fullscreen mode

From this code we link processes in chain task. If any process failed all other processes will die also. We don't need to take care to detect tail/clean task.
We all so make group like:
Process leader --linked--> worker1, worker2, ..., workerN
And any process is failed all other processes will die follow.

trap_exit

In case we don't want a process go to die follow failed process we can turn trap_exit on (set flag to true) and process will receive a failed message instead die follow failed process.

Process.flag(:trap_exit, true)
Enter fullscreen mode Exit fullscreen mode

Your process will receive a message like below when linked to your process failed.

{:EXIT, #PID<0.192.0>, {%RuntimeError{message: "#PID<0.192.0>, raise a RuntimeError :D"}, []}}
Enter fullscreen mode Exit fullscreen mode

monitor process

In other case, we just want to now why other process is crashed we can use monitor process.

Have some function for monitor process: spawn_monitor, Process.monitor for make a monitor to other process.

For remove monitor we can use Process.demonitor.

If monitored process is failed a process make monitor to that process will receive a message like:

{:DOWN, #Reference<...>, :process, #PID<...>, reason}
Enter fullscreen mode Exit fullscreen mode

From link/monitor process we can make a lot of thing for our system and we can sleep well! We can make a our specific supervisor, our pool workers,...

In this time I just a explain in basic, I will explain more detail in the future.

I have LiveBook for similar this you can check a and see content + source demo on our Github repo

I will go to details in other posts.

Thank for reading!

Top comments (0)