Elixir - GenServer 具有其他几个 GenServer pid 的初始状态

标签 elixir

我在自动启动监督树时遇到了死锁问题。一个 GenServer 的初始状态是树中另一个主管的子进程。代码如下:

主管和 worker :

defmodule Parallel.Worker.Supervisor do
  import Supervisor.Spec

  def start_link do
    # Start up a worker for each core
    schedulers = :erlang.system_info(:schedulers)
    children = Enum.map(1..schedulers,
      &(worker(Parallel.Worker.Server, [], id: "Parallel.Worker#{&1}")))

    opts = [strategy: :one_for_one, name: Parallel.Worker.Supervisor]
    Supervisor.start_link(children, opts)
  end

  def workers do
     Process.whereis(Parallel.Supervisor)
      |> Supervisor.which_children
      |> Enum.reduce [], fn
        {_name, pid, :worker, _module}, acc -> [{make_ref, pid} | acc]
        _, acc -> acc
      end
  end

end

GenServer 以及这些工作进程 pid 的状态:

defmodule Parallel.Process.Server do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__, workers: [Parallel.Worker.Supervisor.workers])
  end
end

正如您在最后一行所看到的,我正在调用“Parallel.Worker.Supervisor.workers”,它似乎会阻止等待树上的初始化,直到该方法返回时才会完成。如何将受监督的 PID 作为初始 GenServer 状态?

更新:

我不想使用 poolboy(尽管查看源代码是一个很好的建议)来帮助我了解更多信息。我并不想做任何特别的事情,我的工作人员只是处理传递的函数及其参数。这是工作 GenServer:

defmodule Parallel.Worker do
  use GenServer
  require Logger

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

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

  # Using cast to be async as the job could take longer than the default 5 seconds,
  # Don't want client blocked on waiting for job to complete
  def handle_cast({:execute, fun, args, return_pid, job_ref}, state) do
    Logger.debug fn()-> "#{inspect self}: Recevied job with args: #{inspect args} for job #{inspect job_ref} to return to #{inspect return_pid}" end
    send(return_pid, {job_ref, apply(fun, args), self})
    {:noreply, state}
  end
end

最佳答案

我假设您想在这里创建某种池?正如评论中提到的,你应该看看 poolboy。如果出于练习的目的,您想自己实现这一点,那么仍然值得研究 poolboy 代码以获取灵感。

本质上,poolboy 池由“池管理器”管理 - 一个维护已知工作人员集合的 gen_server。此池管理器进程在内部启动一个 simple_one_for_one 主管,然后用于启动和监督工作人员。

池管理器进程 during initialization first starts the supervisor 。然后,它调用 prepopulate/1start supervised worker processes 。该函数将动态创建N个worker via supervisor:start_child/2 ,并且池管理器可以在内部保留工作进程 pid 列表。

这可以确保池管理器进程在初始化期间不需要与父主管通信(这就是导致死锁的原因)。相反,管理者自己创建 child 。依靠内部监督者仍然可以确保 worker 驻留在监督树中。

还需要一些其他精美的细节来确保一切正常工作。 Poolboy 进程(池管理器)将捕获退出,链接到监视器,并在 worker check out 时对其进行监视。这确保了正确检测工作人员崩溃。我建议阅读代码以进行进一步分析。

我的观点是,这可能是一个有趣的练习,可以更好地理解 OTP。但是,如果您是为了生产而执行此操作,那么直接使用 poolboy 可能会更好。

关于Elixir - GenServer 具有其他几个 GenServer pid 的初始状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27031222/

相关文章:

elixir - 使用 Poison 将映射编码为 JSON 以与 Slack 配合使用

python - Erlport/Python STDOUT 捕获到 Elixir

elixir - 如何使用 Ecto 测试唯一性约束

elixir - phoenix elixir exaws 与 minio 配合使用(上传文件)

dependencies - Elixir - 如何动态获取项目中使用的库版本?

mysql - (Ecto.Query.CompileError) 元组只能用于与相同大小的文字元组进行比较。 - 灵药

elixir - 如何获取Elixir当前的操作系统架构?

erlang - Erlang/Elixir 中网络中断会触发 Monitor_node 或链接断开吗?

function - Elixir,使用另一个模块的功能