Как работает Process.monitor/1 и Process.link/1?

В Elixir функции Process.monitor/1 и Process.link/1 используются для организации взаимодействия между процессами, особенно в части наблюдения за их жизненным циклом и обработки их завершения. Они являются фундаментальными примитивами в модели конкурентности Erlang VM (BEAM), на которой работает Elixir.

Process.link/1

Process.link(pid) устанавливает двухстороннюю связь между текущим процессом и процессом с идентификатором pid. Если один из процессов завершится по какой-либо причине (исключение, вызов exit, завершение узла), то связанный с ним процесс получит сигнал об этом завершении. Если он не настроен на обработку сигнала (trap_exit: true), то он тоже завершится с той же причиной.

Пример:

spawn_link(fn ->
raise "ошибка"
end)

Этот процесс упадёт, и текущий процесс (в котором вызван spawn_link) тоже упадёт.

Использование:

  • Полезно в архитектуре «let it crash», когда процессы связаны логически и один не должен продолжать без другого.

  • Часто используется внутри супервизоров.

  • Работает синхронно: если pid не существует, сразу вызывает исключение.

Удаление связи:

Для удаления связи можно использовать Process.unlink(pid).

Process.monitor/1

Process.monitor(pid) устанавливает одностороннее наблюдение за процессом. Если наблюдаемый процесс завершается, наблюдатель получает сообщение вида:

{:DOWN, ref, :process, pid, reason}

В отличие от link, мониторинг не завершает текущий процесс автоматически — он просто получает сообщение, и дальнейшие действия зависят от вашей логики обработки.

Пример:

pid = spawn(fn -> raise "boom" end)
ref = Process.monitor(pid)
receive do
{:DOWN, ^ref, :process, ^pid, reason} ->
IO.puts("Процесс #{inspect pid} завершился с причиной: #{inspect reason}")
end

Использование:

  • Не требует trap_exit.

  • Подходит для задач, где нужно отслеживать, жив ли другой процесс, но не зависеть от него напрямую.

  • Используется в Task, GenServer, DynamicSupervisor, Registry и других системных конструкциях.

Поведение:

  • Возвращает уникальную ссылку ref, по которой приходит :DOWN.

  • Монитор автоматически снимается после получения :DOWN, или его можно снять вручную с помощью Process.demonitor(ref).

Основные отличия link и monitor

Характеристика Process.link/1 Process.monitor/1
Связь Двусторонняя Односторонняя
--- --- ---
Влияние на завершение Падает оба процесса Только сообщение :DOWN
--- --- ---
Обработка Через trap_exit Через receive
--- --- ---
Используется в Супервизорах, spawn_link Task, динамические системы
--- --- ---
Удаление Process.unlink/1 Process.demonitor/1
--- --- ---
Безопасность Менее безопасный Более безопасный
--- --- ---

Совместное использование

Можно использовать link и monitor вместе, но делать это нужно осознанно. Например, если вы хотите, чтобы процесс был связан (в случае нормальной работы), но при этом могли обрабатывать завершение вручную, а не падать автоматически — тогда можно настроить trap_exit: true и использовать receive.

Обработка падения с trap_exit

defmodule Watcher do
def start do
Process.flag(:trap_exit, true)
pid = spawn_link(fn -> raise "ошибка" end)
receive do
{:EXIT, ^pid, reason} ->
IO.puts("Процесс завершился: #{inspect reason}")
end
end
end

Когда использовать link, а когда monitor

  • link: если процессы тесно связаны логически, и отказ одного должен завершать другой (например, в OTP-супервизорах).

  • monitor: если нужен просто контроль завершения, без риска завершения текущего процесса (например, в асинхронных задачах, отслеживании клиентов в чате, обработке долгоживущих сервисов).