Translate

lunes, 3 de febrero de 2025

Concurrencia en Erlang parte 11


Los monitores son un tipo especial de enlace con dos diferencias:

  • son unidireccionales;
  • se pueden apilar.

Los monitores son lo que necesitas cuando un proceso quiere saber qué está pasando con un segundo proceso, pero ninguno de ellos es realmente vital para el otro.

Otra razón, como se mencionó anteriormente, es apilar las referencias. Ahora bien, esto puede parecer inútil a primera vista, pero es genial para escribir bibliotecas que necesitan saber qué está pasando con otros procesos.

Verás, los enlaces son más una construcción organizacional. Cuando diseñas la arquitectura de tu aplicación, determinas qué proceso hará qué trabajos y qué dependerá de qué. Algunos procesos supervisarán a otros, otros no podrían vivir sin un proceso gemelo, etc. Esta estructura suele ser algo fijo, conocido de antemano. Los enlaces son útiles para eso y no necesariamente deberían usarse fuera de ella.

Pero, ¿qué sucede si tienes 2 o 3 bibliotecas diferentes a las que llamas y todas necesitan saber si un proceso está activo o no? Si usaras enlaces para esto, rápidamente te encontrarías con un problema cada vez que necesitaras desvincular un proceso. Ahora bien, los enlaces no son apilables, por lo que en el momento en que desvinculas uno, los desvinculas a todos y arruinas todas las suposiciones realizadas por las otras bibliotecas. Eso es bastante malo. Por lo tanto, necesitas enlaces apilables, y los monitores son tu solución. Se pueden eliminar individualmente. Además, ser unidireccional es útil en las bibliotecas porque otros procesos no deberían tener que estar al tanto de dichas bibliotecas.

Entonces, ¿cómo se ve un monitor? Bastante fácil, configuremos uno. La función es erlang:monitor/2, donde el primer argumento es el proceso atom y el segundo es el pid:


1> erlang:monitor(process, spawn(fun() -> timer:sleep(500) end)).

#Ref<0.0.0.77>

2> flush().

Shell got {'DOWN',#Ref<0.0.0.77>,process,<0.63.0>,normal}

ok


Cada vez que un proceso que monitorizas deja de funcionar, recibirás un mensaje como este. El mensaje es {'DOWN', MonitorReference, process, Pid, ​​Reason}. La referencia está ahí para permitirte demostrar el proceso. Recuerda, los monitores son apilables, por lo que es posible dejar fuera de servicio más de uno. Las referencias te permiten rastrear cada uno de ellos de una manera única. También ten en cuenta que, al igual que con los enlaces, hay una función atómica para generar un proceso mientras lo monitorizas, spawn_monitor/1-3:


3> {Pid, Ref} = spawn_monitor(fun() -> receive _ -> exit(boom) end end).

{<0.73.0>,#Ref<0.0.0.100>}

4> erlang:demonitor(Ref).

true

5> Pid ! die.

die

6> flush().

ok


En este caso, demostramos el otro proceso antes de que se bloqueara y, por lo tanto, no teníamos rastros de que se hubiera detenido. La función demonitor/2 también existe y brinda un poco más de información. El segundo parámetro puede ser una lista de opciones. Solo existen dos, info y flush:


7> f().

ok

8> {Pid, Ref} = spawn_monitor(fun() -> receive _ -> exit(boom) end end). 

{<0.35.0>,#Ref<0.0.0.35>}

9> Pid ! die.

die

10> erlang:demonitor(Ref, [flush, info]).

false

11> flush().

ok


La opción info le indica si existía o no un monitor cuando intentó eliminarlo. Por eso la expresión 10 devolvió falso. El uso de flush como opción eliminará el mensaje DOWN del buzón si existía, lo que hará que flush() no encuentre nada en el buzón del proceso actual.