我遇到了自动启动监督树的死锁问题.一个GenServer的初始状态是树中另一个主管的子工作者.这是代码:
主管和工人:
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
具有这些工人pids状态的GenServer:
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
sasajuric.. 7
我假设你想在这里创建某种游泳池?正如评论中所提到的,你应该看看poolboy.如果为了练习你想自己实现它,那么仍然值得研究poolboy代码以获得灵感.
实质上,poolboy池由"池管理器"管理 - gen_server维护已知工作者的集合.此池管理器进程在内部启动一个simple_one_for_one
主管,然后用于启动和监督工作人员.
初始化期间的池管理器进程首先启动主管.然后,它调用prepopulate/1
到开始监督工作进程.此函数将通过supervisor:start_child/2动态创建N个worker ,池管理器可以在内部保留worker pid列表.
这可确保池管理器进程在初始化期间不需要与父管理器通信(这会导致您的死锁).相反,经理自己创造孩子.依靠内部主管仍然确保工人居住在监督树中.
还需要一些其他精细打印细节,以确保一切正常.Poolboy进程(池管理器)将捕获出口,链接到监视器,并在签出时监视工作程序.这可确保正确检测工人崩溃.我建议阅读代码以便进一步分析.
我的观点是,这可以是一个有趣的练习,以更好地了解OTP.但是,如果您正在进行生产,那么直接使用poolboy可能会更好.
我假设你想在这里创建某种游泳池?正如评论中所提到的,你应该看看poolboy.如果为了练习你想自己实现它,那么仍然值得研究poolboy代码以获得灵感.
实质上,poolboy池由"池管理器"管理 - gen_server维护已知工作者的集合.此池管理器进程在内部启动一个simple_one_for_one
主管,然后用于启动和监督工作人员.
初始化期间的池管理器进程首先启动主管.然后,它调用prepopulate/1
到开始监督工作进程.此函数将通过supervisor:start_child/2动态创建N个worker ,池管理器可以在内部保留worker pid列表.
这可确保池管理器进程在初始化期间不需要与父管理器通信(这会导致您的死锁).相反,经理自己创造孩子.依靠内部主管仍然确保工人居住在监督树中.
还需要一些其他精细打印细节,以确保一切正常.Poolboy进程(池管理器)将捕获出口,链接到监视器,并在签出时监视工作程序.这可确保正确检测工人崩溃.我建议阅读代码以便进一步分析.
我的观点是,这可以是一个有趣的练习,以更好地了解OTP.但是,如果您正在进行生产,那么直接使用poolboy可能会更好.