如果进程未使用,建议终止该进程的方法是什么?

tag*_*isu 1 elixir

假设我有一个偶尔使用的流程。我应该顺其自然,还是将其状态保存到数据库并在一段时间后将其关闭?有 OTP 方法吗?

我想出了一个计时器进程的想法,它等待特定的时间,然后通知我的工作进程它必须关闭。这样,每个工作进程都会有一个计时器进程,每次工作进程执行某项操作时,它都会通知其计时器进程重置计时器。

tag*_*isu 6

感谢@Mike Quinlan,我找到了一种更好的方法来使用GenServer timeouts来做到这一点。我只是将超时值包含在handle_*回调的每个返回值中。例子:

defmodule Client do
  use GenServer

  @timeout 5000

  def start_link() do
    GenServer.start_link(__MODULE__, nil)
  end

  def hello(pid) do
    GenServer.call(pid, :hello)
  end

  def init(state) do
    {:ok, state}
  end

  def handle_call(:hello, _from, state) do
    {:reply, :hello, state, @timeout}
  end

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

end
Run Code Online (Sandbox Code Playgroud)

每次我打电话时计时器都会重置hello/1。如果我在第一次调用后 5 秒内没有调用hello/1,则进程将关闭。


Mik*_*lan 5

我正在开发一个跟踪用户的应用程序,我们完全按照您的指定进行操作。它将为用户聚合某些值,并在 60 秒后关闭。GenServers有一个回调handle_continue/2,您可以使用它来设置和重置进程的计时器。确保在DynamicSupervisor下启动进程并为它们提供重启策略:temporary

例子:

def MyProc do
  use GenServer, restart: :temporary
  @timeout :timer.seconds(60)

  def start_link(opts) do
    GenServer.start_link(__MODULE__, [], opts)
  end

  def init([]) do
    # This will set the initial state and continue to set_timer
    {:ok, %{timer: nil}, {:continue, :set_timer}}
  end

  def poke(pid) do
    GenServer.call(pid, :poke)
  end

  def do_work(pid) do
    GenServer.cast(pid, :do_work)
  end

  def handle_call(:poke, _from, :state) do
    # We have received a call. Do the work and continue to set_timer
    IO.puts("ouch")
    {:reply, :ok, state, {:continue, :set_timer}}
  end

  def handle_cast(:do_work, state) do
    IO.puts("Working...")
    # You can continue from handle_cast/2 & handle_info/2 and even handle_continue/2
    {:noreply, state, {:continue, :set_timer}}
  end

  def handle_info(:graceful_stop, state) do
    IO.inspect("PID #{inspect(self()} received inactivity shutdown. Bye!")
    {:shutdown, :normal, state}
  end

  def handle_continue(:set_timer, state) do
    # Forget the old timer if it exists
    case state do
      %{timer: timer} when is_reference(timer) -> :timer.cancel(timer)
      _ -> nil
    end
    
    # Send this process the stop message after the timeout expires
    timer_ref = Process.send_after(self(), @timeout, :graceful_stop)
    {:noreply, %{state | timer: timer_ref}}
  end
end
Run Code Online (Sandbox Code Playgroud)

出于多种原因,handle_continue 回调很有用。首先,它不会阻塞回调的调用过程init/1。GenServer 的任何繁重初始化都应该使用handle_continue 完成。其次,它允许进程在回复调用者后继续工作。例如,我们不希望用户等待此进程的响应,因为我们正在重置计时器。第三,它允许轻松共享代码。您所要做的就是{:continue, :set_timer}在每个指示进程应保持活动状态的操作之后放置,并且您可以确信超时将会延长。