Как работает 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: если нужен просто контроль завершения, без риска завершения текущего процесса (например, в асинхронных задачах, отслеживании клиентов в чате, обработке долгоживущих сервисов).