发送消息让许多Genserver同时行动,知道他们都行动了吗?

时间:2018-12-16 20:41:51

标签: elixir

我创建了许多GenServer,并使用PID一次向它们发送消息。但是,我希望他们在游戏的一个“回合”中大致同时行动。我该如何:(1)播放“去!”给他们的消息(2)知道他们已经完成了表演(即转身结束了吗?)?

2 个答案:

答案 0 :(得分:1)

一种可以实现所需目标的方法是cast发出所有go消息,然后异步答复:

defmodule TurnTracker do
  use GenServer

  def init(pids) do
    state = %{
      pids: pids,
      ongoing_requests: MapSet.new()
    }
    {:ok, state}
  end

  # This will send out your go message to all genservers
  def handle_call(:broadcast, _from, state) do

    Enum.each(state.pids, fn pid ->
      GenServer.cast(pid, {:go, self()})
    end)

    # The ongoing_requests is just a collection of all of the pids for the Player processes.  One-by-one they will be removed using the handle_cast underneath here.
    updated_state = Map.put(state, :ongoing_requests, MapSet.new(state.pids))

    {:reply, :ok, updated_state}
  end

  # When one of the genservers is done its turn, it will send a message to here
  def handle_cast({:completed_turn, pid}, state) do

    # Remove the pid from the set, eventually we will remove them all
    ongoing_requests = MapSet.delete(state.ongoing_requests, pid)
    updated_state = Map.put(state, :ongoing_requests, ongoing_requests)

    # Check to see if that was the last one, if it was, all of the Players are done their turns
    if MapSet.size(ongoing_requests) == 0 do
      # All of your GenServers are done
    else
      # One or more are not done yet
    end

    {:noreply, updated_state}
  end
end


# You will have a bunch of these
defmodule Player do
  use GenServer

  def handle_cast({:go, turn_tracker_pid}, state) do
    # Your turn logic here

    # Tell the TurnTracker that we are done
    GenServer.cast(turn_tracker_pid, {:completed_turn, self()})

    {:noreply, state}
  end
end

实际上并没有一种方法可以保证您的GenServer同时运行,因为当您发送邮件时,您只是在将邮件放入其邮箱中,而在您的邮箱中可能还会有其他邮件。

如果转弯时间超过5秒(Genserver.call的默认超时时间),那么:broadcast将会在此处超时。

答案 1 :(得分:0)

对于最简单的解决方案,我将使GenServer消息同步(通过使用call而不是cast发送消息),并在每个服务器上启动Task等待结果(以免阻塞原始的呼叫过程并能够同时触发许多消息)。基本上是这样的:

servers
|> Enum.map(fn server -> Task.async(fn -> GenServer.call(server, :go) end) end)
|> Enum.map(&Task.await/1)

请注意,Task.await具有默认超时时间,因此,如果您的转弯时间很长,则可能需要增加超时时间。