Преамбула
В предыдущих частях (часть 1, часть 2) мы полностью рассмотрели механизм создания двунаправленных связей между процессами и процесс распространения ошибок. В данной статье будет разобран, достаточно простой, механизм мониторов и ещё несколько аспектов касающихся работы с процессами.
Мониторы
Функции для работы с мониторами
Небольшой перечень функций, которые могут понадобиться для работы с мониторами:
- erlang:monitor/2 — вызывающий функцию процесс начинает мониторинг указанного процесса;
- erlang:demonitor/1/2 — отключение функции мониторинга;
- erlang:spawn_monitor/1/3 — создание нового процесса и привязка вызывающего функцию процесса в качестве монитора.
Если наблюдаемый процесс падает, то монитор получает сообщение {'DOWN', Reference, process, Pid, Reason}, где
- 'DOWN' — означает что процесс упал;
- Reference — ссылка на монитор;
- process — объектом мониторинга является процесс (в документации написано, что в текущей версии эрланга, мониторить можно только процессы, возможно, в будущем прикрутят что-то ещё);
- Pid — Pid упавшего процесса;
- Reason — причина.
Одно из отличий создания связи и монитора заключается в том, что если вы попытаетесь создать связь с несуществующим процессом, вызывающий процесс упадет, а в случае монитора вы сразу получите сообщение {'DOWN', Reference, process, Pid, Reason}. Давайте попробуем это на практике — запускаем eshall и выполняем следующие команды.
(emacs@aleksio-mobile)1> self().
<0.36.0>
(emacs@aleksio-mobile)2> erlang:link(c:pid(0,777,0)).
** exception error: no such process or port
in function link/1
called as link(<0.777.0>)
(emacs@aleksio-mobile)3> self().
<0.39.0>
(emacs@aleksio-mobile)4> flush().
ok
(emacs@aleksio-mobile)5> erlang:monitor(process, c:pid(0,777,0)).
#Ref<0.0.0.43>
(emacs@aleksio-mobile)6> self().
<0.39.0>
(emacs@aleksio-mobile)7> flush().
Shell got {'DOWN',#Ref<0.0.0.43>,process,<0.777.0>,noproc}
ok
(emacs@aleksio-mobile)8>
В первой строке мы узнаем Pid оболочки, затем пытаемся связать текущий процесс с заведомо не существующим erlang:link(c:pid(0,777,0)), после чего оболочка падает, в очереди сообщений пусто. Пятой командой вызываем функцию erlang:monitor(process, c:pid(0,777,0)), которая создает и возвращает ссылку на монитор. Так как процесса с Pid = <0.777.0> не существует в очередь оболочки приходит сообщение {'DOWN',#Ref<0.0.0.43>,process,<0.777.0>,noproc}, которое говорит нам, что процесса <0.777.0> в системе нет.
Напишем небольшой модуль, для закрепления знаний о мониторах.
-module(testm).
-export([start_m/0, stop_m/1, loop/1]).
start_m() ->
erlang:spawn_monitor(?MODULE, loop, [self()]).
stop_m(Ref) ->
erlang:demonitor(Ref).
loop(Shell) ->
receive
kill -> exit(kill);
reason -> exit("Another reason");
Msg -> Shell ! {get_msg, Msg}
end.
Функция spawn_monitor создает процесс с телом loop и делает вызывающий процесс (в нашем примере shell) монитором.
(emacs@aleksio-mobile)2> {Pid, Ref} = testm:start_m().
{<0.43.0>,#Ref<0.0.0.62>}
###Процесс создан <0.43.0>, ссылка на монитор #Ref<0.0.0.62>
(emacs@aleksio-mobile)3> Pid ! hello.
hello
###Отправляем процессу сообщение
(emacs@aleksio-mobile)4> flush().
Shell got {get_msg,hello}
Shell got {'DOWN',#Ref<0.0.0.62>,process,<0.43.0>,normal}
ok
###В ответ процесс нам отправляет сообщение {get_msg,hello}
###И завершает свою работу с причиной normal
(emacs@aleksio-mobile)5> f(Pid), f(Ref).
ok
###Делаем переменные Pid и Ref свободными
###Дальше все по аналогии
(emacs@aleksio-mobile)6> {Pid, Ref} = testm:start_m().
{<0.48.0>,#Ref<0.0.0.77>}
(emacs@aleksio-mobile)7> Pid ! kill.
kill
(emacs@aleksio-mobile)8> flush().
Shell got {'DOWN',#Ref<0.0.0.77>,process,<0.48.0>,kill}
ok
(emacs@aleksio-mobile)9> f(Pid), f(Ref).
ok
(emacs@aleksio-mobile)10> {Pid, Ref} = testm:start_m().
{<0.53.0>,#Ref<0.0.0.91>}
(emacs@aleksio-mobile)11> Pid ! reason.
reason
(emacs@aleksio-mobile)12> flush().
Shell got {'DOWN',#Ref<0.0.0.91>,process,<0.53.0>,"Another reason"}
ok
(emacs@aleksio-mobile)13> testm:stop_m(Ref).
true
(emacs@aleksio-mobile)14>
Вопрос к читателям: как вы заметили при каждом новом создании процесса ссылка на монитор тоже новая, но отключение монитора мы сделали только в конце. Останутся ли предыдущие ссылки где-либо в состоянии нашего процесс и может ли это как-нибудь повлиять на работу?
Заключение
Мы рассмотрели мониторы — достаточно удобный и простой механизм слежения за состоянием других процессов. Надеюсь, каждый сможет применить их в своих задачах, реализованных на языке эрланг.
Что почитать?
1. Отличная интерактивная документация.
2. ERLANG Programming by Francesco Cesarini and Simon Thompson.
3. Статья на RSDN: обработка ошибок в Erlang.