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