Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Elixir 发送一条消息,让多个GenServer同时运行,并知道它们都已运行?_Elixir - Fatal编程技术网

Elixir 发送一条消息,让多个GenServer同时运行,并知道它们都已运行?

Elixir 发送一条消息,让多个GenServer同时运行,并知道它们都已运行?,elixir,Elixir,我创建了许多GenServer,并使用PIDs一次向它们发送一条消息。但是,我希望他们在一个游戏中的一个“回合”上几乎同时行动。我如何才能:(1)向他们广播一条“开始!”消息(2)知道他们都演完了(即,结束了)?实现目标的一种方法是将所有的开始消息抛出,然后异步回复: defmodule TurnTracker do use GenServer def init(pids) do state = %{ pids: pids, ongoing_reques

我创建了许多GenServer,并使用PIDs一次向它们发送一条消息。但是,我希望他们在一个游戏中的一个“回合”上几乎同时行动。我如何才能:(1)向他们广播一条“开始!”消息(2)知道他们都演完了(即,结束了)?

实现目标的一种方法是将所有的
开始
消息抛出,然后异步回复:

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
将在此超时。

对于最简单的解决方案,我将使
Genserver
消息同步(使用
call
而不是
cast
发送消息)并为每台服务器启动一个
任务
,等待结果(以避免阻止原始调用过程,并能够同时触发多条消息)。基本上是这样的:

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

请注意,
Task.await
有一个默认超时,因此如果您的轮换时间很长,您可能需要增加该超时时间。

我如何:(1)向他们广播“开始!”消息(2)知道他们已全部完成表演(即轮换结束)?如果GenServer收到“go”消息,然后立即崩溃怎么办?除非链接/监视GenServer进程,否则您将不知道GenServer是否仍在工作或崩溃。那么,如果GenServer崩溃,您想做什么?事情会变得相当复杂——这就是为什么会有OTP的原因……提出OTP的程序员在这些复杂的场景中做了所有的艰苦工作,你可以通过使用OTP来利用他们的辛勤工作。如果要重新启动崩溃的GenServer并使其完成工作,则可以使用OTP管理器。不过,尝试用手去做事情来接触复杂的事物并不是一件坏事。在解决方案的每次迭代中,都会有人指出您没有解决的边缘情况和缺陷。@7stud,我非常感谢您的建议,并将与OTP主管一起工作。现在我更关心的是时间而不是撞车。谢谢@Tyler。我想可能有一个特殊的功能,可以向多个PID进行广播。迭代集合是一种常见的解决方案。在你的例子中,玩家没有向TurnTracker注册,因此我不明白TurnTracker如何在每个回合开始时生成其PID列表。我将
PID
列表添加到TurnTracker的
init
,我想你可以在启动时传递它们(因此,首先启动所有玩家)。