En

Enlaces en Elixir

1 ejercicio

Acerca de Enlaces

Los procesos de Elixir están aislados y, de forma predeterminada, no comparten nada. Cuando un proceso hijo no enlazado falla, su proceso padre no se ve afectado.

parent_pid =
  spawn(fn ->
    spawn(fn -> raise "oops" end)

    receive do
      message -> IO.inspect(message, label: "received message")
    end
  end)

# => 20:03:08.405 [error] Process #PID<0.153.0> raised an exception
#    ** (RuntimeError) oops
#    (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6

Process.alive?(parent_pid)
# => true

Este comportamiento se puede cambiar enlazando procesos entre sí. Si dos procesos están enlazados, un fallo en uno de ellos se propagará al otro. Los enlaces son bidireccionales.

parent_pid =
  spawn(fn ->
    spawn_link(fn -> raise "oops" end)

    receive do
      message ->
        IO.inspect(message, label: "received message")
    end
  end)

# => 20:05:34.125 [error] Process #PID<0.171.0> raised an exception
#    ** (RuntimeError) oops
#    (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6

Process.alive?(parent_pid)
# => false

Los procesos se pueden crear ya enlazados al proceso que realiza la llamada usando spawn_link/1, que es una operación atómica, o se pueden enlazar más tarde con Process.link/1.

Puedes comprobar qué procesos están enlazados actualmente a un proceso (y siguen en ejecución) con Process.info/1.

pid = spawn_link(fn -> :timer.sleep(50_000) end)
# => #PID<0.126.0>

self()
|> Process.info()
|> Keyword.get(:links)

# => [#PID<0.126.0>]

Enlazar procesos puede resultar útil al hacer trabajo en paralelo, cuando cada fragmento de trabajo no debería continuar en caso de que otro fragmento no llegue a completarse.

Capturar las salidas

El enlace también se puede usar para supervisar procesos. Si un proceso captura las salidas, no se bloqueará cuando falle un proceso al que está enlazado. En su lugar, recibirá un mensaje sobre el fallo. Esto le permite gestionar el fallo de forma controlada, por ejemplo reiniciando el proceso que ha fallado.

Un proceso se puede configurar para capturar las salidas llamando a Process.flag(:trap_exit, true). Ten en cuenta que Process.flag/2 devuelve el valor anterior del flag, no el nuevo.

El valor actual del flag se puede comprobar con Process.info/1.

pid
|> Process.info()
|> Keyword.get(:trap_exit)

# => false

El mensaje que se enviará al proceso en caso de que falle un proceso enlazado coincidirá con el patrón {:EXIT, from, reason}, donde from es un PID. Si reason es algo distinto del átomo :normal, significa que el proceso falló o fue terminado por la fuerza.

parent_pid =
  spawn(fn ->
    Process.flag(:trap_exit, true)

    spawn_link(fn -> raise "oops" end)

    receive do
      message ->
        IO.inspect(message, label: "received message")

        # do something else,
        # to demonstrate that the process stays alive
        :timer.sleep(50_000)
    end
  end)

# => 20:05:34.125 [error] Process #PID<0.295.0> raised an exception
#    ** (RuntimeError) oops
#    (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6

# received message: {:EXIT, #PID<0.295.0>,
#   {%RuntimeError{message: "oops"},
#     [{:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 678]}]}}

Process.alive?(parent_pid)
# => true

Ten en cuenta que capturar las salidas hace que el proceso sea inmune a los fallos de todos sus procesos enlazados, y que los mensajes de salida podrían acumularse en el buzón del proceso si no se leen. Por este motivo, deberías llevar un registro de todos los procesos enlazados a un proceso que captura salidas. Normalmente, los procesos que capturan salidas solo se encargan de supervisar otros procesos y no hacen nada más.

En la práctica, no implementarías tú mismo un proceso supervisor desde cero. Elixir proporciona una abstracción para este tipo de funcionalidad en forma del comportamiento Supervisor.

Otras abstracciones en torno a los procesos, como Agent, GenServer o Task, ofrecen dos formas de iniciarlos: start o start_link.

Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Enlaces