如何在Erlang / OTP中将主管的子pid分享给另一个孩子

时间:2012-11-22 22:19:23

标签: erlang otp

在我的Erlang / OTP应用程序中,我有一个one_for_all主管( sup ),其中有几个孩子。其中一个孩子( child1 gen_server行为)应该能够向另一个孩子发送消息( child2 ,行为为supervisor)。当然,我可以注册它,但用多余的名字堵塞全球范围似乎不是一个好主意。

因此,使这种交互成为可能的唯一方法是使用 child2 的pid提供 child1 。说到做到。有supervisor:wich_children/1调用具有适当的功能。只需将 sup 的pid作为 chidl1 的参数传递,在which_children中调用child1:init,然后......获得死锁。 sup 正在等待 child1 启动, child1 正在等待 sup 以获取子描述:

init(SupPid) ->
    Descriptions = supervisor:which_children(SupPid),
    ... .

这可以通过以下方法解决:

init(SupPid) ->
    gen_server:cast(self(), initialize),
    ... .

handle_cast(initialize, State) ->
    Descriptions = supervisor:which_children(SupPid),
    ...  % Generating new state containing desired pid
    {noreply, NewState}.

但是,我对此解决方案并不满意。

问题是:根据OTP设计原则,监管树成员之间最常见的交互方式是什么?

2 个答案:

答案 0 :(得分:5)

当然,你还不能向主管询问有关其孩子的问题,但还没有启动它们:)

实际上,注册(在本地,使用erlang:register())并不是一个坏主意。此外,如果在child1中处理原始pid,你应该手动设置child2 pid的监控,以便能够对可能的崩溃等作出反应,但是注册后你就可以直接通过名字来询问。

如果没有注册,您可以推迟通知孩子直到主管:start_link被调用:

start_link() ->
    R=supervisor:start_link({local, ?SERVER}, ?MODULE, []),

    %% Here supervisor is started so you can notify its children
    R.

答案 1 :(得分:1)

这很大程度上取决于您的业务逻辑,但如果您担心会堵塞全球范围,我的建议是您考虑使用的监管策略。

在这种情况下,当两个对等进程需要以这种方式关联时,我看到的是创建了一个额外的控制器进程来管理这种关联,而simple_one_for_one支持用于生成两个对等进程之一(gen_server最有可能)。当使用进程注册时,可以再次使用此技术,例如当您将有许多运行相同代码的gen_servers实例时。

该技术基本上包括使用simple_one_for_one管理程序来生成gen_server。基本上发生的事情是,启动时(simple_one_for_one sup)不会立即生成任何子进程,但只有在您明确调用supervisor:start_child(Sup, List)时才会生成。

然后你的child2初始化逻辑(甚至你的业务逻辑在某一点上)可以通过supervisor:start_child(Sup, List)生成gen_server并被child2 Pid所提供,这样他们就可以毫不费力地进行通信。

额外的控制器进程保留了gen_server之间关系的字典,由一些唯一性标识,但避免了全局空间堵塞,因为这只是控制器的本地空间。然后,您可以要求控制器在逻辑上的任何位置分配或取消分配此关系。

监督树因此看起来像:

supervision tree

您可以在github上的tinymq项目中使用此实现读取代码

如果您以前从未使用过simple_one_for_one主管,那么子规范可能有点棘手,请记住,您可以通过supervisor:start_child(Sup, List)上的{{1}}列表将child2的pid传递给gen_server。 Args one,您在子规范中指定的。

simple_one_for_one supervisors

我的两分钱!