GenServer :continue 调用是同步的吗?

Is a GenServer :continue call synchronous?

A GenServer handle_call/3 实现可以 return :continue 调用附加函数。是否可以保证此函数何时 运行 相对于其他消息?

例如,考虑这个只保留一个 运行ning 计数器的模块:

defmodule Tmp do
  use GenServer

  def start_link(opts), do: GenServer.start_link(__MODULE__, 0, opts)
  def incr(tmp), do: GenServer.call(tmp, :incr)

  @impl true
  def init(state), do: {:ok, state}

  @impl true
  def handle_call(:incr, _from, n) do
    {:reply, n, n, {:continue, :incr}}
  end

  @impl true
  def handle_continue(:incr, n) do
    {:noreply, n+1}
  end
end

当您调用 Tmp.incr/1 时,handle_call/3 方法 return 是计数器的当前值,但随后 return 也是 :continue。这会导致 GenServer 基础架构调用 handle_continue/2.

如果我连续两次调用Tmp.incr/1,我保证得到递增的值吗?或者 handle_call/3 是否有可能在 handle_continue/2 被调用之前被调用两次?

iex> {:ok, tmp} = Tmp.start_link([])
iex> Tmp.incr(tmp)
0
iex> Tmp.incr(tmp)
1
# If I type fast enough, will this ever return 0?

是的,:continue 是同步的 - continue 将在您从中返回的函数后立即执行。它的use-case就是:保证紧接着就是运行,这是其他方法无法保证的,比如:timeout或者给自己发消息send(self(), :incr)

这在the documentation for GenServer callbacks中有简要提及:

Returning {:reply, reply, new_state, {:continue, continue}} is similar to {:reply, reply, new_state} except handle_continue/2 will be invoked immediately after with the value continue as first argument.

也在 the Timeout section:

Because a message may arrive before the timeout is set, even a timeout of 0 milliseconds is not guaranteed to execute. To take another action immediately and unconditionally, use a :continue instruction.