Az Elixir folyamatai izoláltak, és alapértelmezés szerint nem osztanak meg semmit egymással. Amikor egy nem összekapcsolt gyermekfolyamat összeomlik, a szülőfolyamatát ez nem érinti.
parent_pid =
spawn(fn ->
spawn(fn -> raise "oops" end)
receive do
message -> IO.inspect(message, label: "received message")
end
end)
# => 20:03:08.405 [error] Process #PID<0.153.0> raised an exception
# ** (RuntimeError) oops
# (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6
Process.alive?(parent_pid)
# => true
Ez a viselkedés megváltoztatható, ha a folyamatokat összekapcsoljuk egymással. Ha két folyamat össze van kapcsolva, az egyik folyamat hibája átterjed a másikra. Az összekapcsolás kétirányú.
parent_pid =
spawn(fn ->
spawn_link(fn -> raise "oops" end)
receive do
message ->
IO.inspect(message, label: "received message")
end
end)
# => 20:05:34.125 [error] Process #PID<0.171.0> raised an exception
# ** (RuntimeError) oops
# (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6
Process.alive?(parent_pid)
# => false
A folyamatokat elindíthatod már eleve a hívó folyamathoz kapcsolva is a spawn_link/1 segítségével, ami atomi művelet, vagy később is összekapcsolhatod őket a Process.link/1 hívással.
A Process.info/1 segítségével megnézheted, mely folyamatok vannak éppen egy adott folyamathoz kapcsolva (és futnak még).
pid = spawn_link(fn -> :timer.sleep(50_000) end)
# => #PID<0.126.0>
self()
|> Process.info()
|> Keyword.get(:links)
# => [#PID<0.126.0>]
A folyamatok összekapcsolása akkor lehet hasznos, ha párhuzamosított munkát végzel, és egy munkarészt nem érdemes folytatni, ha egy másik nem tud befejeződni.
Az összekapcsolás folyamatok felügyeletére is használható. Ha egy folyamat elkapja a kilépéseket, nem omlik össze, amikor egy vele összekapcsolt folyamat összeomlik. Helyette üzenetet kap az összeomlásról. Így szépen kezelheti az összeomlást, például újraindíthatja az összeomlott folyamatot.
Egy folyamat beállítható úgy, hogy elkapja a kilépéseket, ha meghívod a Process.flag(:trap_exit, true) hívást. Vedd figyelembe, hogy a Process.flag/2 a flag régi értékét adja vissza, nem az újat.
A flag aktuális értékét a Process.info/1 segítségével ellenőrizheted.
pid
|> Process.info()
|> Keyword.get(:trap_exit)
# => false
Az az üzenet, amelyet a folyamat kap, ha egy összekapcsolt folyamat összeomlik, a {:EXIT, from, reason} mintára illeszkedik, ahol a from egy PID. Ha a reason bármi más, mint a :normal atom, az azt jelenti, hogy a folyamat összeomlott, vagy erőszakkal leállították.
parent_pid =
spawn(fn ->
Process.flag(:trap_exit, true)
spawn_link(fn -> raise "oops" end)
receive do
message ->
IO.inspect(message, label: "received message")
# do something else,
# to demonstrate that the process stays alive
:timer.sleep(50_000)
end
end)
# => 20:05:34.125 [error] Process #PID<0.295.0> raised an exception
# ** (RuntimeError) oops
# (stdlib 3.13.2) erl_eval.erl:678: :erl_eval.do_apply/6
# received message: {:EXIT, #PID<0.295.0>,
# {%RuntimeError{message: "oops"},
# [{:erl_eval, :do_apply, 6, [file: 'erl_eval.erl', line: 678]}]}}
Process.alive?(parent_pid)
# => true
Vedd figyelembe, hogy a kilépések elkapása miatt a folyamatot a vele összekapcsolt folyamatok összes összeomlása sem érinti, és a kilépési üzenetek felgyűlhetnek a folyamat postaládájában, ha nem olvassák el őket. Ezért tartsd számon az összes olyan folyamatot, amely egy kilépéseket elkapó folyamathoz van kapcsolva. A kilépéseket elkapó folyamatok általában csak más folyamatok felügyeletéért felelősek, és mással nem foglalkoznak.
A gyakorlatban egy felügyelő folyamatot nem neked kell a nulláról megvalósítanod. Az Elixir egy absztrakciót kínál erre a funkcióra a Supervisor viselkedés formájában.
A folyamatok körüli egyéb absztrakciók, például az Agent, a GenServer vagy a Task, kétféle módon indíthatók el: start vagy start_link hívással.