【问题标题】:Killing GenServer from inside从内部杀死 GenServer
【发布时间】:2016-09-29 02:29:32
【问题描述】:

考虑这样简单的 GenServer 模块:

defmodule Crap do
  use GenServer

  ... #All the needed crap

  def handle_info(:kill_me_pls, state) do
    GenServer.stop(self)
    {:noreply, state}
  end

  def terminate(_, state) do
    IO.inspect "Look! I'm dead."
  end

end

并考虑将这些表达式放入 repl:

{:ok, pid} = Crap.start_link

send_message_to_that_pid

这就是我的想法开始的地方,因为Process.alive? pid 返回 true,但进程没有响应,terminate 没有被调用,尽管如果我在 repl 中调用 GenServer.stop(pid) 调用“干净”进程(有未收到终止消息)正确终止它。如果 stop 调用收到 :kill_me_pls 消息的进程会挂起 repl。

【问题讨论】:

    标签: elixir


    【解决方案1】:

    “从内部”停止 GenServer 的正确方法是从 handle_* 返回 {:stop, reason, new_state} 值或从 handle_call 返回 {:stop, reason, reply, new_state}。您的代码失败的原因是调用 GenServer.stop 会向 GenServer 发送一条特殊消息,并且由于 GenServer 是单个进程并且当前位于 handle_info 中,因此它无法响应 stop 调用,因此您的进程挂起/创建僵局。

    这是正确的方法:

    def handle_info(:kill_me_pls, state) do
      {:stop, :normal, state}
    end
    

    您可以阅读更多关于可能的返回值的信息,查看documentation of GenServer 中标题Return values 下的文本。

    演示:

    iex(1)> defmodule Crap do
    ...(1)>   use GenServer
    ...(1)>
    ...(1)>   def handle_info(:kill_me_pls, state) do
    ...(1)>     {:stop, :normal, state}
    ...(1)>   end
    ...(1)>
    ...(1)>   def terminate(_, state) do
    ...(1)>     IO.inspect "Look! I'm dead."
    ...(1)>   end
    ...(1)> end
    iex(2)> {:ok, pid} = GenServer.start Crap, []
    {:ok, #PID<0.98.0>}
    iex(3)> send(pid, :kill_me_pls)
    "Look! I'm dead."
    :kill_me_pls
    iex(4)> Process.alive?(pid)
    false
    

    【讨论】:

    • 值得一提的是链接已经失效,退出代码的文档在Supervisor文档中。此外,如果您希望任何子进程也停止,那么您将返回 {:stop, :shutdown, state}
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 2014-10-20
    • 1970-01-01
    • 2021-10-24
    相关资源
    最近更新 更多