【发布时间】:2019-10-04 09:34:08
【问题描述】:
我发现当尝试捕获退出信号时,使用 GenServer.start 然后 Process.link 启动 GenServer 的 pid 与运行 GenServer.start_link 的结果截然不同。
这是我用来演示问题的实验代码:
defmodule Foo do
defmodule Server do
def init(_) do
Process.flag(:trap_exit, true)
{:ok, nil}
end
def handle_info({:EXIT, from, reason}, _) do
IO.inspect({:kill_signal, from, reason})
{:noreply, nil}
end
end
def foo() do
Process.flag(:trap_exit, true)
# version 1
{:ok, pid} = GenServer.start_link(Server, nil)
# version 2
# {:ok, pid} = GenServer.start(Server, nil)
# Process.link(pid)
# print process info
IO.inspect({self(), pid, Process.info(pid)})
Process.exit(pid, :reason)
:timer.sleep(200)
end
end
Foo.foo
对于版本 1,EXIT 信号会导致 Server 退出而不会被其 handle_info 块捕获,但对于版本 2,信号在 handle_info 块中被正确拦截和处理,因此 Server没有终止。我还注意到这两种启动 GenServer 的方式中的Process.info 是相同的。
我对@987654331@ 和spawn 进行了同样的尝试,但它们的行为都符合预期——EXIT 信号全部被捕获——没有区别:
defmodule Foo do
def loop do
Process.flag(:trap_exit, true)
receive do
msg -> IO.inspect(msg)
end
loop
end
def foo() do
Process.flag(:trap_exit, true)
# version 1
pid = spawn_link(&loop/0)
# version 2
# pid = spawn(&loop/0)
# Process.link(pid)
# print process info
IO.inspect({self(), pid, Process.info(pid)})
Process.exit(pid, :reason)
:timer.sleep(200)
end
end
Foo.foo
顺便说一句,如果重要的话,我在 Erlang/OTP 21 上使用 Elixir 1.8.1。
我想知道导致行为差异的原因是什么,这是一个错误还是设计使然,以及如果我想以原子方式调用 start+link,如何正确捕获 EXIT。
【问题讨论】:
标签: erlang elixir erlang-otp gen-server