我创建了许多GenServer,并使用PID一次向它们发送消息。但是,我希望他们在游戏的一个“回合”中大致同时行动。我该如何:(1)播放“去!”给他们的消息(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
具有默认超时时间,因此,如果您的转弯时间很长,则可能需要增加超时时间。