Преамбула
В предыдущих частях (часть 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.
