使用 start_link 启动时 GenServer 不会 trap_exit

Sho*_* Ya 1 erlang elixir erlang-otp gen-server

我发现当尝试捕获退出信号时,使用pid 启动 GenServer 的结果与GenServer.start运行.Process.linkGenServer.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
Run Code Online (Sandbox Code Playgroud)

在版本 1 中,EXIT 信号导致Server退出而不会被捕获在其handle_info块中,但在版本 2 中,信号在handle_info块中被正确拦截和处理,因此Server不会终止。我还注意到Process.info这两种启动 GenServer 的方法是相同的。

spawn_link我尝试了与和相同的操作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
Run Code Online (Sandbox Code Playgroud)

顺便说一句,如果重要的话,我在 Erlang/OTP 21 上使用 Elixir 1.8.1。

我想知道是什么导致了行为差异,这是一个错误还是设计使然,以及如果我想原子地调用 start+link,如何正确捕获 EXIT。

Jos*_*lim 6

handle_info没有被调用,因为发送退出信号的是父进程。GenServer 和所有其他行为处理父级退出信号,并且总是在父级退出信号时关闭,主要是因为如果您位于监督树中并且您的主管关闭,您也希望立即终止,因为在那一刻所有的赌注都结束了。如果你替换这个:

Process.exit(pid, :reason)
Run Code Online (Sandbox Code Playgroud)

经过:

spawn fn -> Process.exit(pid, :reason) end
Run Code Online (Sandbox Code Playgroud)

您可以看到handle_info当另一个进程正在发送退出信号时被调用。

  • 谢谢你的澄清; 恕我直言,这个片段在关于 OTP 流程模型与“孤立”流程有何不同的文档/指南中非常有意义。 (2认同)