handle_info :DOWN 在被监控的进程结束后不被调用

handle_info :DOWN is not called after monitored process dies

我遇到了 Process.monitor/1 的问题。我最初的用例是监控 Phoenix Channel 并在它结束后进行一些清理。但是,我没能在 Phoenix 中进行设置,因此决定使用纯 GenServers 对其进行测试。

所以,我有一个简单的 GenServer,我想跟踪它何时死亡:

defmodule Temp.Server do
  use GenServer

  def start_link(_), do: GenServer.start_link(__MODULE__, %{})

  def init(args) do
    Temp.Monitor.monitor(self())
    {:ok, args}
  end
end

还有另一个监控的 GenServer:

defmodule Temp.Monitor do
  use GenServer
  require Logger

  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def monitor(pid) do
    Process.monitor(pid)
  end

  def handle_info({:DOWN, ref, :process, _, _}, state) do
    Logger.info("DOWN")

    {:noreply, state}
  end
end

所以,如果我没理解错的话,Process.monitor会开始监听Temp.Server进程,并且应该在Server进程时调用匹配:DOWNhandle_info死了。如果我在 iex:

中尝试
iex> {_, pid} = Temp.Server.start_link([])
{:ok, #PID<0.23068.3>}                    
iex> Process.exit(pid, :kill)             
true     

我希望从 Monitor 模块调用 handle_info 并记录 "DOWN",但是这并没有发生。我究竟做错了什么?我认为它不起作用,因为我从服务器进程 Temp.Monitor.monitor(self()) 调用监视器,但我不知道我还应该怎么做。

当你调用Temp.Monitor.monitor/1方法时,它仍然是运行在Temp.Server自己的进程中,而不是Temp.Monitor的。这意味着当 Temp.Server 死亡时 :DOWN 消息被发送到 Temp.Server,这是多余的。

您想要做的是,将您的服务器进程的 pid 传递给 Temp.Monitor 并让它从它自己的进程中调用 Process.Monitor 方法,以便它可以监视它。这只能发生在 GenServer callbacks.

之一

您可以通过将实施移至 handle_call/3 or handle_cast/3:

来做到这一点
defmodule Temp.Monitor do
  use GenServer
  require Logger

  def start_link(_) do
    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def monitor(pid) do
    GenServer.cast(__MODULE__, {:monitor, pid})
  end

  def handle_cast({:monitor, pid}, state) do
    Process.monitor(pid)
    {:noreply, state}
  end

  def handle_info({:DOWN, ref, :process, _, _}, state) do
    Logger.info("DOWN")

    {:noreply, state}
  end
end