handle_info:监视的进程死后不调用DOWN

时间:2018-11-15 19:59:08

标签: process elixir monitor

我遇到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进程,并在handle_info进程终止时调用与:DOWN相匹配的Server。如果我在iex中尝试过:

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

我希望从handle_info模块调用Monitor并记录“ DOWN”,但是不会发生。我究竟做错了什么?我认为这是行不通的,因为我从服务器进程Temp.Monitor.monitor(self())进行监视,但我只是想不出该怎么做。

1 个答案:

答案 0 :(得分:1)

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

您要做的是将服务器进程的pid传递到Temp.Monitor,并使其从自己的进程中调用Process.Monitor方法,以便它可以对其进行监视。这只能从GenServer callbacks之一发生。

您可以通过将实现移至handle_call/3handle_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