Erlang / OTP如何通知父进程子进程空闲且邮箱中没有消息

时间:2014-12-20 02:28:38

标签: erlang otp

我想设计一个流程层次结构,其中有一个父流程P,它像一个网守,并将工作(来自其客户流程的消息/事件)委托给它的子流程C1,C2..Cn它们相互协作并可能将结果发送回P.儿童流程无法与外部任何流程进行通信,只有P。

挑战在于虽然P可能有来自其客户端的多条消息,但它应该只接受一条消息,将工作委托给C1..Cn,并且只接受来自其客户的下一条消息

  • 当所有子进程完成(或空闲)并且C1到Cn之间没有更多消息传播时。
  • P完成接受来自C1..Cn的消息,以便它可以将结果返回给其客户端

约束:

  • 对我来说,闲暇时他们正在等待接收(阻止)或者只是退出。
  • C1到Cn是有限状态机。他们中的一些或全部可能会将消息发送回C.或者可能没有消息要发送回C.即使没有消息被发送回C,C也必须弄清楚所有消息都是在没有消息的情况下完成的。它们。
  • 如果C1到Cn中的任何一个已被抢占,那么它被认为是忙碌的(这可能是显而易见的,但我认为我会把它放在这里完成)而且C将不会收到下一条消息

是否有OTP模式或库可以为我做这件事(在我破解之前?)。我知道process_info可以让我知道进程的邮箱是否为空,我可以继续检查来自P的子邮箱但是从P进行不必要的轮询。

编辑概述:我正在尝试在Erlang平台上实现Flow Based Programming的反应变体。这有“等级过程”的概念。或复合本身可能包含复合过程,直到我们到达一些实际代码框...我将研究(查看monitor,process_info,process_flag)但我想回答你的优秀答案

编辑递归父母:C1和Cn中的每一个本身都可以是父/复合过程。如果我只是生成流程并让它们立即退出,我每次都必须创建复合材料链,因为C1..Cn本身可能是复合材料(产生复合材料......等等)。最后,当我们到达一个叶子框(它不是一个复合节点)时,它们应该是有限状态机。所以我不确定是否产生并且如果它们是FSM则使它们快速退出。

编辑TKOWAL:因为我正在尝试创建一个通用的父/复合过程,所以它不知道'何时'任务结束。它所做的一切就是将它从孩子那里收到的信息传递给它的兄弟姐妹,并带有“约束”。它不会接受来自其客户/兄弟姐妹的下一条消息,直到它们的孩子完成了#39;孩子C1..Cn不仅可以发送一个而且可以发送许多消息。我从你的提议中了解到,wait_for_task_finish将在收到第一条消息时停止阻止。但P的孩子也可能发出更多信息。 P应该等待所有消息。此外,具有task_end符号将不会出于同样的原因(即可能来自孩子的多条消息)

3 个答案:

答案 0 :(得分:2)

考虑到启动Erlang流程的成本是多么便宜,你的看门人可以为每个传入的任务启动新的孩子,然后等待他们一旦完成工作就正常退出。

但总的来说,听起来你正在寻找一个流程池。其中有一些已经可用,例如poolboysidejob。池可以是harder to get right than you think,因此我建议在尝试编写自己的池之前使用现有的经过验证的池实现。

答案 1 :(得分:1)

编辑后,这成了完全不同的问题,所以我发布了新答案。

如果您正在尝试编写基于流程的编程,那么您可能正在解决错误的问题。 FBP很有效,因为几乎所有东西都是异步的,你在完成上一个请求后立即开始处理下一个请求。

所以,答案是 - 不要等孩子完成:

  

在FBP中,组件之间没有时间依赖关系。所以,如果我   拥有一大块数据,它应该能够从一端流出   无论其他任何数据如何,都可以向另一方绘制图表   被处理。为了对FBP系统进行编程,您必须最小化   你的依赖。

source

创建父级和子级时,您知道块之间的所有连接,因此只需将子级配置为将处理后的数据直接发送到下一个块。例如:P1有孩子C1和C2。您向P1发送消息,它将其委托给C1,数据包在C1和C2之间流动几次,之后,C1或C2直接将其发送到P2。

块应该是无状态的。它们的输出不应该依赖于先前的请求,所以即使C1和C2正在处理来自两个不同请求的数据到P1 - 也没关系。可能存在这样的情况,其中P1获得数据包D1然后获得D2,但是将以不同的顺序输出答案R2然后输出R1。这也没关系。您可以使用Erlang reference标记邮件,然后检查哪个响应来自哪个请求。

答案 2 :(得分:0)

我不认为,有一个现成的库,但它很容易入侵,除非我错过了什么。您的P流程应如下所示:

ready_for_next_task() ->
    receive
        {task, Task, CallerPid} ->
            send_task_to_workers(Task)
    end,
    wait_for_task_finish(CallerPid).

wait_for_task_finish(CallerPid) ->
    receive
        {task_end, Response} ->
            CallerPid ! Response
    end,
    ready_for_next_task().

wait_for_task_finish/1中,您只有一个接收子句,因此它不会接受下一个任务,直到当前的任务完成。如果您正在等待工作人员的多个回复,您可以简单地将第二个子句添加到receive并进行部分响应,并递归调用wait_for_task_finish/1

最好有一些指标,即处理结束,因为您不能保证邮件传递时间。这意味着,你可以检查,所有进程当前正在等待消息并思考,他们已经结束处理,但实际上,他们还没有开始,或者其中一个向其他人发送消息,你在第二个拥有它之前抓住了它们在消息框中。

如果过程C1..Cn只有部分实际工作并且不知道进度,那么看门人P应该知道有多少部分,然后逐个接收所有部分,然后致电{ {1}}。