【问题标题】:How can I get the state of a child after it exited?退出后如何获取孩子的状态?
【发布时间】:2015-02-15 07:26:15
【问题描述】:

我正在制作一个程序,其中孩子具有某些值,但可以随时接收die 消息使其退出。此外,我有一个监控过程,可以将自己与孩子们联系起来,检查他们是否还活着,并阻止他们的出口。如果孩子已经死亡,监视器将收到退出消息,重新注册孩子并在必要时使用相同的状态/值重新生成它。监视器这样做是为了让所有孩子都可以通过他们注册的名字找到,就好像他们从未停止过一样。

所以我的问题是:

  • 既然父级困住了子级退出,那么子级退出后是否还能联系到子级,得到当前状态?还是孩子无论如何都退出了?
  • 如何重新注册具有相同名称和值的孩子?我应该记录所有孩子的价值观,还是有其他方法可以通过一些 Erlang 魔法来检索这些价值观?

【问题讨论】:

    标签: erlang


    【解决方案1】:

    听起来你正在重新实现supervisor。如果不是出于教育目的,请阅读supervision

    如果您只是在学习,那么这里就是答案。您写道“父母被困孩子无法退出”。诱捕出口不是那样工作的。进程只能为自己设置trap_exit 标志。当其他进程试图用exit(Pid, Reason) 终止它时,会使用它。调用process_flag(trap_exit, true) 的进程不会死亡,而是会收到一条消息。它甚至可以忽略它并继续工作。

    例如,您可以这样使用:child 正在捕获出口,当有人试图关闭它时,它会收到消息并调用exit({Reason, State})。这将确保监控过程获得孩子的状态,并通知它正在死亡。然后它可以重新生成具有初始状态的新进程并重新注册它。

    回答您的问题:您无法联系已退出的进程。您必须生成新进程,提供以前的状态,然后再次注册。

    【讨论】:

    • 这确实是出于教育目的(阅读:家庭作业),我按照您所说的方式通过退出消息发送状态来实现它。我不得不承认,这感觉就像一个肮脏的黑客比要走的路……或者这会是 Erlang 的方式吗?我确实有一个过程告诉孩子们死了,他们必须调用exit(killed),所以我把它改成了exit({killed,State}),但不确定是否会这样。跨度>
    • 这很hacky,因为作业要求你做你不应该做的事情。如果进程终止,则意味着它遇到错误(可能是由于状态不佳)、挂起并被外部杀死(可能是由于状态不佳)或正常终止,因此不需要该状态。您永远不应该从退出进程中获取状态,因为它可能是错误的并且会感染您的应用程序的其余部分。
    • 那么假设它崩溃了,我怎么能恢复孩子的价值呢?孩子是否应该不断向父母更新其价值观?
    • 进程有单一职责是好的。让父母成为监督者,只监视和重生垂死的孩子。产生兄弟姐妹可能是个好主意。一个只负责保持状态,第二个负责计算。如果它们在不同的节点上(以及不同机器上的节点) - 你有很好的容错能力。在重生它们中的任何一个之前,您可以联系另一个以提取初始状态。
    【解决方案2】:

    它退出后是否还能联系到孩子 -> 不能。一旦进程死亡,一切都被 erlang VM 清理。

    我怎样才能重新注册同名的孩子 -> 是的,如果进程真的死了,没有限制重新使用同名进行进程注册。这其实是常见的情况。

    我怎样才能用相同的值重新注册孩子 -> 没有简短的回答。

    • 首先,只有在 ets 或 mnesia 或任何外部平均值(您的流程之外)中注册此流程的状态时,您才能获得该流程的状态 - 您将获得最后存储的值
    • 或者,如果您的进程捕获到 die 消息并将所有需要的信息发送回其“自制主管”。它会在您的设计中引入复杂性,并且并非所有导致死亡的原因都可以被捕获(至少 kill 消息不能)。
    • 最后:为什么要这样做。听起来您应该处理应用程序的数据模型。有一些数据属于您的应用程序,并且必须在进程崩溃(客户帐户、订单......)中幸存下来。在大多数情况下,它应该能够在应用程序完全停止或服务器崩溃后继续存在。为此制作了数据库(带有复制?)。这些数据不是进程状态。另一方面,进程状态应该只对当前的进程有意义,它可以是一个套接字、一些引用、pids、一些超时......你应该注意从 init 参数重建所有内容,而不是试图恢复以前的状态,仅仅是因为大多数时候它已经过时了,也可能是因为其中一个数据可能是导致崩溃的原因。这种区别对于拥有没有副作用的代码(更容易调试和验证)以及坚持“让它崩溃”用法很重要。

    【讨论】:

    • 为什么?因为我们的教授希望我们处理 Erlang 中的失败。所以我猜你可以假设这个过程会崩溃,而不是干净地死去。我们必须使用 Erlang 来实现 2048 游戏,其中 4 x 4 网格中的每个图块都是一个注册进程。根据命令(上、下、左、右),这些图块必须相互通信以更新其值,但图块可以随机强制关闭。所以我们学会了如何应对失败。我们应该使用当前值重新生成图块,就好像它们从未消失一样。
    • 查看此演示文稿:slideshare.net/torbenhoffmann90/2014-12ndcthinking 它指导如何设计基于细胞的生命游戏。处理失败从幻灯片 29 开始。它有点复杂,但解决方案的扩展性非常好。
    • 这是一个很好的练习,你仍然可以问为什么。每个单元的作用可能是知道它的位置、它的邻居、应用游戏规则来对玩家的行为做出反应,并确保结果存储在 mnesia 中。然后,您可以通过直接接口或全局接口实现冗余记忆表来存储游戏进度,以便在每一轮的单个原子动作中同步所有更新(以保持游戏一致性)。这取决于您如何对故障进行建模。一个简单的Pid ! die 将在一个已知的地方被捕获(在模具工作之前保存)但exit(Pid,kill) 不会。
    【解决方案3】:

    sys:get_state/1 允许您访问仍在运行的进程的状态。如果进程正在对其状态中包含的数据做一些重要的事情,那么数据也应该存储在其他地方——磁盘上、数据库中或另一个进程中(可能是消息队列?)。 这可能是一个架构问题。您应该计划单个进程崩溃,并且您不需要挑选死进程的剩余部分来恢复。你没有提到主管,这可能是你应该研究的。监督者是容错和并发 Erlang/OTP 应用程序的重要组成部分。有关主管行为的文档可在此处获得:http://www.erlang.org/doc/man/supervisor.html

    两个问题的答案:您可能应该考虑重新设计您的应用程序,以便主管处理进程的重新启动,然后可能创建另一个进程来管理不应丢失的数据/状态。

    【讨论】:

    • sys 仅适用于正确的 OTP 进程,例如 gen_server。它不适用于任何进程。例如在控制台中尝试:sys:get_state(self()).
    猜你喜欢
    • 2020-02-27
    • 2014-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-22
    • 2018-02-28
    • 2014-01-29
    相关资源
    最近更新 更多