Линк

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