Линк — двусторонняя связь (зависимость) между двумя акторами, проявляющаяся в том, что в случае ошибки в одном акторе, другой тоже рухнет. То же: link.
Если зависимость односторонняя, то это монитор (односторонний линк).
Текущий актор устанавливает линк с другим актором с помощью бифа link(Pid). Убирается линк с помощью бифа unlink(Pid). Если, как часто бывает, линк с другим актором создаётся сразу после спауна этого актора, то категорически рекомендуется использовать биф spawn_link, который единой операцией спаунит актор и создаёт с ним линк. В противном случае возможна ситуация, когда созданный актор успеет рухнуть до того, как с ним был установлен линк.
Чтобы увидеть линк в действии, попробуем такой пример:
-module(alexustas).
-export([alex/0, ustas/1]).
alex() ->
io:format("Alex приступил к работе.~n"),
_Ustas = spawn_link(?MODULE, ustas, [self()]),
alex_loop().
alex_loop() ->
receive
Mess ->
io:format("[Alex] Получено сообщение: ~ts~n", [Mess]),
alex_loop()
after 20_000 ->
io:format("[Alex] Давно не получал сообщений.~n"),
alex_loop()
end.
ustas(Alex) ->
io:format("Ustas приступил к работе.~n"),
ustas_loop(Alex).
ustas_loop(Alex) ->
Rand = rand:uniform(20),
case Rand of
5 -> Alex ! "ЮСТАС АЛЕКСУ НАХОЖУСЬ НА СВЯЗИ";
7 -> Alex ! "ЮСТАС АЛЕКСУ ВСЕ ИДЕТ ПО ПЛАНУ";
10 -> Alex ! "ЮСТАС АЛЕКСУ ПРИШЛИТЕ НОВЫЕ НОСКИ";
13 -> Alex ! math:sqrt(-Rand);
_ -> []
end,
receive
Mess ->
io:format("[Ustas] Получено сообщение: ~ts~n",
[Mess]),
ustas_loop(Alex)
after 1000 -> ustas_loop(Alex)
end.
В нём мы создаём два актора с позывными Алекс и Юстас. Мы будем спаунить Алекса, а тот — Юстаса. Между Алексом и Юстасом сразу будет установлен линк. Юстас какое-то время будет слать сообщения Алексу, но потом, выкинув 13, терпит крах. Будет ли крах у Алекса?
erl +pc unicode
1> spawn(alexustas, alex, []).
<0.86.0>
Alex приступил к работе.
Ustas приступил к работе.
[Alex] Получено сообщение: ЮСТАС АЛЕКСУ НАХОЖУСЬ НА СВЯЗИ
[Alex] Получено сообщение: ЮСТАС АЛЕКСУ ПРИШЛИТЕ НОВЫЕ НОСКИ
2> is_process_alive(<0.86.0>).
true
[Alex] Получено сообщение: ЮСТАС АЛЕКСУ ВСЕ ИДЕТ ПО ПЛАНУ
[Alex] Получено сообщение: ЮСТАС АЛЕКСУ НАХОЖУСЬ НА СВЯЗИ
=ERROR REPORT==== 16-Dec-2024::18:49:46.178563 ===
Error in process <0.88.0> with exit value:
{badarith,[{math,sqrt,[-13],[{error_info,#{module => erl_stdlib_errors}}]},
{alexustas,ustas_loop,1,[{file,"alexustas.erl"},{line,29}]}]}
3> is_process_alive(<0.86.0>).
false
Мы дважды используем биф is_process_alive/1, чтобы проверить, жив ли Алекс или нет. Сначала он жив, но после того, как Юстас попытался взять корень из -13, уже нет. Мы воочию убедились, как работает линк.
Слинкованный актор рушится, только если его партнёр терпит крах. Если другой актор просто завершит свою работу, его партнёр продолжит работать. Закомментируем в нашем примере в конце блок receive ... end, чтобы ustas_loop не зацикливалась. Юстас штатно завершит свою работу, а Алекс будет работать и жаловаться, что давно не получал сообщений.
7> l(alexustas).
{module,alexustas}
8> spawn(alexustas, alex, []).
Alex приступил к работе.
<0.97.0>
Ustas приступил к работе.
[Alex] Давно не получал сообщений.
[Alex] Давно не получал сообщений.
[Alex] Давно не получал сообщений.
[Alex] Давно не получал сообщений.
Линк активно используется в надзоре, где одни акторы следят за другими.
Copyright © 2025 Алексей Карманов